<?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://danieloz147.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://danieloz147.github.io/" rel="alternate" type="text/html" /><updated>2026-07-26T20:24:54+00:00</updated><id>https://danieloz147.github.io/feed.xml</id><title type="html">Daniel Oz | Security Research</title><subtitle>Offensive security research. Writing about vulnerabilities, how they were discovered, attack surfaces, exploitation, and breaking modern systems.</subtitle><author><name>Daniel Oz</name></author><entry><title type="html">Audio Is Not Just Output. It Is a Channel.</title><link href="https://danieloz147.github.io/2026/07/21/audio-is-not-just-output-its-a-channel/" rel="alternate" type="text/html" title="Audio Is Not Just Output. It Is a Channel." /><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://danieloz147.github.io/2026/07/21/audio-is-not-just-output-its-a-channel</id><content type="html" xml:base="https://danieloz147.github.io/2026/07/21/audio-is-not-just-output-its-a-channel/"><![CDATA[<div class="research-credit-note">
  <p class="research-credit-label">Full Engineering Version</p>
  <p>If you want the full communication-engineering analysis, measurements, and design tradeoffs, continue to <a href="/2026/07/21/think-like-an-attacker-when-the-speaker-becomes-a-communication-channel/">Deep Dive: When the Speaker Becomes a Communication Channel</a>.</p>
</div>

<h2 id="tldr">TL;DR</h2>

<ul>
  <li>Attackers should think in terms of information transfer, not product features.</li>
  <li>In Citrix and VDI, audio is not just sound output. It is a communication channel.</li>
  <li>The real challenge was not writing code. It was understanding what the channel preserves and what it destroys.</li>
  <li>The channel, not preference, dictated the final architecture.</li>
  <li>If you want the full communication-engineering treatment, read the deep dive: <a href="/2026/07/21/think-like-an-attacker-when-the-speaker-becomes-a-communication-channel/">/2026/07/21/think-like-an-attacker-when-the-speaker-becomes-a-communication-channel/</a>.</li>
</ul>

<h2 id="introduction" class="post-h2-center post-h2-emphasis">Introduction</h2>

<p>Most security reviews begin by listing features: file transfer, clipboard, USB redirection, browser access, audio, printing, and so on. The assumption is simple: if enough features are blocked, the attack surface becomes small enough to manage.</p>

<p>That is often a useful defensive view. It is not how an attacker thinks.</p>

<p>An attacker usually does not care whether a system exposes a feature called file transfer. The real question is more fundamental: can information still cross the boundary?</p>

<p>That shift sounds semantic, but it changes everything. Once you stop thinking in terms of named features and start thinking in terms of channels, you stop asking what the product claims to allow. You start asking what the system still makes physically possible.</p>

<p>Citrix environments are a good example. You can disable the clipboard, block drive mapping, prevent USB passthrough, and still leave several paths that must remain available for normal operation. Users still need to see the remote desktop. They still need to hear alerts. They still need to provide input. Every one of those paths is a form of information transfer.</p>

<p>That is the key idea behind this research. Audio was not interesting because it was a forgotten feature. It was interesting because it was still a live channel.</p>

<figure class="figure-centered">
  <img src="/assets/images/citrix-audio/image1.png" alt="A split scene where defensive controls block common transfer paths while an attacker uses audio as a communication channel" />
  <figcaption>Blocking features does not always block channels</figcaption>
</figure>

<h2 id="the-mindset-shift" class="post-h2-center post-h2-emphasis">The Mindset Shift</h2>

<p>The most important change in this project happened before a single line of code was written.</p>

<p>At first, the problem looked like an offensive-security question: can data be moved across a VDI boundary through sound?</p>

<p>Very quickly, that stopped being the real question.</p>

<p>The real question became this: if Citrix audio is treated as a communication channel, what does that channel actually preserve? Which parts of a signal survive the trip? Which parts are destroyed by the codec, the operating system, the transport path, and the endpoint audio stack?</p>

<p>That is a very different problem. It is no longer about finding a trick. It is about characterizing a medium.</p>

<p>Once you look at it that way, the work changes completely. Instead of choosing a clever modulation method first, you start by measuring the channel. You ask which frequencies survive, whether amplitude stays stable, whether phase remains usable, how much noise is introduced, and how short a symbol can be before the codec smears it beyond recognition.</p>

<p>In other words, the project moved from offensive security into communication engineering.</p>

<div class="post-fact-callout">
  <p class="post-fact-label">Fact 01</p>
  <p class="post-fact-text">The best modulation is not the one that carries the most information, but the one that survives the channel best.</p>
</div>

<h2 id="what-broke" class="post-h2-center post-h2-emphasis">What Broke First</h2>

<p>One of the first assumptions to collapse was the idea that the most advanced-looking modulation scheme would automatically be the best choice.</p>

<p>On paper, complex modulation looks powerful. It promises better spectral efficiency, more bits per symbol, and higher theoretical throughput. But those advantages only matter if the receiver can still distinguish the states after the signal has passed through the real channel.</p>

<p>That was not what happened here.</p>

<p>The Citrix audio path did not behave like a clean textbook channel. The codec introduced its own preferences. Some frequency regions held up better than others. Some signal properties survived reasonably well. Others degraded enough to become unreliable. At that point, theory stopped being the deciding factor.</p>

<p>Measurement took over.</p>

<div class="post-fact-callout">
  <p class="post-fact-label">Fact 02</p>
  <p class="post-fact-text">I did not choose the architecture. The channel chose it for me.</p>
</div>

<p>At this stage, that became the most useful mental model. The channel dictated what could survive. The codec dictated what remained distinguishable. The noise dictated how much ambition was realistic.</p>

<figure class="figure-centered">
  <img src="/assets/images/citrix-audio/image4.png" alt="Channel-driven architecture constraints in practice" />
  <figcaption>The channel dictated the architecture more than theoretical preference did</figcaption>
</figure>

<h2 id="why-measurements-mattered" class="post-h2-center post-h2-emphasis">Why Measurements Mattered</h2>

<p>The deepest lesson from this work was not about Citrix specifically. It was about methodology.</p>

<p>At almost every stage, intuition was less useful than direct measurement.</p>

<p>Ideas that sounded superior in theory often performed worse in practice. More carriers did not always help. More states did not always mean more usable bits. A scheme that looked less elegant on paper sometimes transferred more real information, simply because it survived the channel better.</p>

<p>That is the point where this stopped being an exercise in cleverness and became an engineering problem. Instead of asking what should work, the right question became: what does the channel prove that it can carry?</p>

<div class="post-fact-callout">
  <p class="post-fact-label">Fact 03</p>
  <p class="post-fact-text">Measurements are always better than intuition.</p>
</div>

<h2 id="what-came-out-of-it" class="post-h2-center post-h2-emphasis">What Came Out of It</h2>

<p>The result was not just a rough proof of concept. Over time, it became a full communication system: compression, framing, synchronization, modulation, error correction, interleaving, validation, and recovery.</p>

<p>That system became VDI Bridge.</p>

<p>It was not built by starting with a perfect design and then coding it faithfully. It was built by failing, measuring, adjusting, and narrowing the solution space until only the combinations compatible with the real channel remained.</p>

<p>If you want to see the implementation layer, the demonstration, and the transmission pipeline, the deep dive goes through the engineering decisions in detail:</p>

<ul>
  <li>Full deep dive: <a href="/2026/07/21/think-like-an-attacker-when-the-speaker-becomes-a-communication-channel/">/2026/07/21/think-like-an-attacker-when-the-speaker-becomes-a-communication-channel/</a></li>
  <li>Code: <a href="https://github.com/danieloz147/VDI-Bridge/">https://github.com/danieloz147/VDI-Bridge/</a></li>
</ul>

<figure class="figure-centered">
  <img src="/assets/images/citrix-audio/image5.png" alt="Measurement-driven refinement of channel communication strategy" />
  <figcaption>The implementation was the result of constraints, not preference</figcaption>
</figure>

<h2 id="what-is-worth-remembering" class="post-h2-center post-h2-emphasis">What Is Worth Remembering</h2>

<ul>
  <li>Attackers should think about channels, not just named features.</li>
  <li>A system can remain restrictive at the product level while still exposing usable paths for information transfer.</li>
  <li>Good measurements are more valuable than elegant assumptions.</li>
  <li>The architecture of a communication system is often dictated by the channel itself.</li>
  <li>If you want the full engineering analysis, continue to the deep dive: <a href="/2026/07/21/think-like-an-attacker-when-the-speaker-becomes-a-communication-channel/">/2026/07/21/think-like-an-attacker-when-the-speaker-becomes-a-communication-channel/</a>.</li>
</ul>]]></content><author><name>Daniel Oz</name></author><category term="offensive-security" /><category term="citrix" /><category term="vdi" /><category term="covert-channel" /><category term="red-team" /><category term="research" /><summary type="html"><![CDATA[A shorter, more accessible version of the VDI audio research story, focused on the mindset shift from features to communication channels.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danieloz147.github.io/assets/images/citrix-audio/image1.png" /><media:content medium="image" url="https://danieloz147.github.io/assets/images/citrix-audio/image1.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Deep Dive: When the Speaker Becomes a Communication Channel</title><link href="https://danieloz147.github.io/2026/07/21/think-like-an-attacker-when-the-speaker-becomes-a-communication-channel/" rel="alternate" type="text/html" title="Deep Dive: When the Speaker Becomes a Communication Channel" /><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://danieloz147.github.io/2026/07/21/think-like-an-attacker-when-the-speaker-becomes-a-communication-channel</id><content type="html" xml:base="https://danieloz147.github.io/2026/07/21/think-like-an-attacker-when-the-speaker-becomes-a-communication-channel/"><![CDATA[<div class="research-credit-note">
  <p class="research-credit-label">Start Here First</p>
  <p>If you want the shorter and lighter version of this story before the full engineering analysis, start with <a href="/2026/07/21/audio-is-not-just-output-its-a-channel/">Audio Is Not Just Output. It Is a Channel.</a></p>
</div>

<h2 id="tldr">TL;DR</h2>

<ul>
  <li>Offensive Security starts with questions, not tools.</li>
  <li>In Citrix and VDI, the real problem is not the feature itself, but whether information can still cross the boundary.</li>
  <li>The audio path is not a vulnerability in the usual sense; it is a legitimate communication channel with measurable constraints.</li>
  <li>The codec, not the modulation, became the real bottleneck.</li>
  <li>Measurements mattered more than intuition, and simple designs survived better than sophisticated ones.</li>
  <li>Writing the code was the easy part; understanding the channel was the hard part.</li>
</ul>

<nav class="post-toc" aria-label="Table of contents">
  <h3>Table of Contents</h3>
  <ol>
    <li><a href="#introduction">Introduction</a></li>
    <li>
      <a href="#breaking-down-the-problem">Breaking Down the Problem</a>
      <ol>
        <li>
          <a href="#understanding-the-physics-before-writing-code">Understanding the Physics Before Writing Code</a>
          <ol>
            <li><a href="#what-is-a-wave">What Is a Wave?</a></li>
            <li><a href="#the-three-core-properties-of-any-wave">The Three Core Properties of Any Wave</a></li>
            <li>
              <a href="#how-does-a-wave-carry-information">How Does a Wave Carry Information?</a>
              <ol>
                <li><a href="#codec">Codec</a></li>
                <li><a href="#noise">Noise</a></li>
              </ol>
            </li>
          </ol>
        </li>
        <li><a href="#time-to-stop-guessing">Time to Stop Guessing</a></li>
      </ol>
    </li>
    <li>
      <a href="#and-then-everything-broke">And Then... Everything Broke</a>
      <ol>
        <li><a href="#more-complex-better">More complex = better</a></li>
        <li><a href="#the-channel-decides-not-you">The channel decides. Not you.</a></li>
        <li><a href="#measurement-beats-intuition">Measurement beats intuition</a></li>
      </ol>
    </li>
    <li><a href="#vdi-bridge">VDI Bridge</a></li>
    <li><a href="#what-is-worth-remembering-from-this-article">What Is Worth Remembering From This Article</a></li>
  </ol>
</nav>

<h2 id="introduction" class="post-h2-center post-h2-emphasis">Introduction</h2>

<p>One of the clearest differences between offensive and defensive thinking is not the tooling, the technical depth, or even the experience of the people involved. It starts earlier, with how each side defines the problem it is trying to solve.</p>

<p>In many organizations, security is translated into a list of capabilities to allow or block. VDI is a good example. An administrator can choose whether file transfer is allowed, whether the clipboard is blocked, whether local drives are mapped, or whether USB devices are permitted. At first glance, this seems like a reasonable way to evaluate security posture. The more capabilities you block, the smaller the attack surface appears.</p>

<p>Attackers, however, rarely think in terms of capabilities. From their perspective, a capability is just one implementation of a more basic idea: information transfer.</p>

<p>This shift, from capability-based thinking to channel-based thinking, marks one of the main differences between penetration testing and Red Team or Adversary Emulation thinking. Instead of asking whether files can be transferred, the first question becomes whether any mechanism still allows information to cross the boundary. It may sound like a semantic change, but it materially reshapes both system analysis and opportunity discovery.</p>

<p>Citrix environments illustrate this well. You can block all common file-sharing mechanisms and still leave channels that are essential for normal operation. Users still need to see the remote desktop, hear alerts, and provide keyboard input. Each mechanism serves a different functional purpose, but from an engineering standpoint they share the same property: they transfer information from one point to another.</p>

<p>Once this perspective is adopted, audio stops being just “sound output” and becomes a communication channel. More broadly, any component that can transfer information between two sides stops being just a feature and starts being a channel. It is a small conceptual shift on paper, but it has a large effect on offensive reasoning.</p>

<p>It is important to emphasize that this article is not about a Citrix vulnerability and does not present a “break” in the conventional sense. The audio channel behaves as designed and does not rely on a faulty implementation. The research is not about bypassing security through a bug. Instead, it examines how a legitimate path can be analyzed as a communication channel with defined engineering characteristics. That distinction matters because it highlights the difference between exploiting vulnerabilities and leveraging inherent system properties.</p>

<p>This is the starting point of the research described in this article.</p>

<p>The goal of this research was not simply to test whether exfiltration can be done through sound. The deeper question was whether Citrix audio can be treated as a communication channel in a full engineering sense, and which physical and technical constraints determine data rate, reliability, and resilience to codec-induced distortion.</p>

<p>The transition from an Offensive Security question to a Communication Engineering question happened before a single line of code was written. Most later decisions, from modulation choice, to protocol structure, to error-correction design, were driven by channel characteristics rather than by preference for any specific technology.</p>

<p>This is also the central message of the article. Although it ultimately presents a communication system operating over the Citrix audio path, it is not about audio alone. It is about the mindset shift that leads an attacker to stop viewing system components as isolated functionality and start viewing them as measurable communication channels with physical constraints.</p>

<p>Answering this question required stepping, at least temporarily, outside the cyber domain and into communication engineering. Only after understanding the physical principles governing the audio channel was it possible to make informed decisions about how to use it.</p>

<figure class="figure-centered">
  <img src="/assets/images/citrix-audio/image1.png" alt="A split scene where defensive controls block common transfer paths while an attacker uses audio as a communication channel" />
  <figcaption>Figure 1 - Blocking features does not always block channels</figcaption>
</figure>

<h2 id="breaking-down-the-problem" class="post-h2-center post-h2-emphasis">Breaking Down the Problem</h2>

<p>After deciding to stop thinking in terms of capabilities and start thinking in terms of communication channels, it became clear that the next step was not writing code. In fact, writing code was one of the final stages in the process.</p>

<p>In any communication system, there is a fundamental question that must be answered before almost any other engineering decision: what is the nature of the channel available to me?</p>

<p>This may sound like a trivial question, but in practice it dictates almost everything that follows. Modulation strategy, transmission rate, error-correction design, packet structure, and even synchronization logic are all derived from channel characteristics, not from preference for a specific technology.</p>

<p>This is also where I see many studies start in the opposite direction. A modulation method is often chosen first, a proof of concept is built, and only then do people check whether that method is even suitable for the channel. To me, that is backward. Modulation is a solution to an engineering problem; it is not the starting point.</p>

<p>So before writing a single line of code, I tried to understand what really moves between both sides of the VDI boundary.</p>

<p>At that stage, the Citrix environment itself was almost secondary. Communication was the primary focus.</p>

<p>If we look at the audio path abstractly, it is simply a mechanism that moves a wave from one point to another. From a physical perspective, there is no essential difference between a radio wave, Wi-Fi, Bluetooth, or sound. In all cases, energy is transmitted over time, and the only question is how to make variations in that energy represent information. That is, in essence, the definition of a communication system.</p>

<p>The information itself is meaningless to the channel. The channel does not know whether we are trying to send a Word document, a ZIP file, an image, or a video. For the channel, everything is reduced to a bitstream. This means the real problem is not how to transfer a file, but how to map bits into physical variations that can be generated, transmitted, and reliably detected on the receiving side.</p>

<p>At that point, it became clear that the real objective was not building an exfiltration tool, but building a communication system. In classical communication-engineering terms, that means building a modem: a system that converts digital information into a signal suitable for a physical medium, sends it through a constrained channel, and reconstructs it at the receiver.</p>

<p>Once I viewed the problem this way, the work order changed completely. Instead of asking “which modulation is best?” I started with much more fundamental questions:</p>

<ul>
  <li>What is a wave, and how can it be used to carry information?</li>
  <li>How can digital data be represented on top of a wave?</li>
  <li>How do we convert a file into a sequence of physical signal changes?</li>
  <li>What is modulation, and why are there so many modulation methods?</li>
  <li>What is the channel bandwidth, and how does it constrain transmission rate?</li>
  <li>What is signal-to-noise ratio (SNR), and why is it one of the most important parameters in any communication system?</li>
  <li>Which frequencies survive channel passage, and which are attenuated or lost entirely?</li>
  <li>Does the channel preserve all signal properties, or only some of them? Can we rely on phase, amplitude, frequency, or only on signal presence?</li>
  <li>Does the channel behave linearly, or does it alter the signal as a function of intensity, content, or other attributes?</li>
  <li>At what sampling rate does the channel operate, and how does this impact transferable information?</li>
  <li>What is the maximum throughput the channel can support, and where is the true system bottleneck?</li>
</ul>

<p>In retrospect, these were the most important questions in the entire research. From that point on, I was no longer looking for tricks to bypass system limits. I was trying to understand the limits themselves, because every limit you understand becomes a design constraint you can work with.</p>

<p>In other words, before deciding how to transmit, you must first understand what the channel is capable of carrying.</p>

<p>For that reason, the next chapter is not about Citrix or PowerShell. It is about wave physics. Before choosing modulation, setting transmission rates, or designing a protocol, you need to understand the physical properties of waves, which properties can be modified to represent information, and which properties survive channel passage. Only after that foundation can a communication system be designed in an informed way.</p>

<p>Warning note: the next chapter is very dense. If you do not want to dive into the physics behind the process, you can skip the physics chapter.</p>

<h3 id="understanding-the-physics-before-writing-code">Understanding the Physics Before Writing Code</h3>

<p>Before we can understand how to transmit information through sound, we need to start with a more fundamental principle: information does not “move” by itself. It is always represented through a change in some physical phenomenon.</p>

<p>When we send a WhatsApp message, browse the web, or listen to music over Bluetooth, there are no literal bits floating through the air. What actually propagates is a change in a physical property within a medium. In Wi-Fi, that medium is electromagnetic waves. In fiber optics, it is light. In an audio path, it is pressure variation in air.</p>

<p>In other words, a communication system does not transfer information directly; it transfers energy, while information is encoded through changes in that energy. This is the foundation of every modern communication system.</p>

<h4 id="what-is-a-wave">What Is a Wave?</h4>

<p>A wave is a disturbance that propagates through space and transfers energy from one location to another, without transporting the material itself.</p>

<p>When a speaker plays music, it does not “send air” to the listener. The speaker diaphragm moves back and forth, creating periodic pressure changes in the air. Each air molecule moves slightly around its local position, transfers energy to neighboring molecules, and returns to nearly the same place. The result is a sound wave propagating through space.</p>

<p>The same principle applies to radio waves, light waves, and other communication forms. The difference is not in the idea itself, but in the type of energy being transferred and the medium in which it propagates.</p>

<h4 id="the-three-core-properties-of-any-wave">The Three Core Properties of Any Wave</h4>

<p>Every periodic wave has three basic properties we can control:</p>

<ul>
  <li>Frequency: Frequency is the number of cycles a wave completes per second, measured in hertz (Hz). A frequency of 1000 Hz means the wave completes one thousand cycles every second. In hearing terms, this is perceived as pitch. Higher frequency generally sounds higher.</li>
  <li>Amplitude: Amplitude represents the strength of the wave’s oscillation. In sound waves, this maps to loudness. The farther the speaker diaphragm moves, the larger the pressure variation and the louder the perceived sound.</li>
  <li>Phase: Phase describes where the wave is within its cycle relative to a reference point. If two waves have the same frequency and amplitude but one starts slightly later, they can look nearly identical while still being phase-shifted relative to one another.</li>
</ul>

<p>Phase is not usually perceived directly by human hearing, but it is critically important in communication systems because it can carry additional information without changing frequency or amplitude.</p>

<div class="wave-lab" id="waveLab" dir="ltr" data-wave-theme="light">
  <div class="wave-header">
    <div>
      <h3>The Anatomy of a Wave</h3>
      <p>Amplitude, frequency and phase describe the signal.</p>
    </div>

    <button type="button" id="wavePauseButton">Pause</button>
  </div>

  <div class="wave-lab-note" role="note" aria-label="Learning note">
    <strong>Learning note:</strong>
    This visualization is an abstraction of the core terms. It simplifies the model on purpose, but real mastery of waves requires many more concepts, definitions, and edge cases than what is shown here.
  </div>

  <div class="wave-lab-toolbar">
    <label class="wave-toggle" for="waveFormulaToggle">
      <input id="waveFormulaToggle" type="checkbox" />
      <span>Show live formula</span>
    </label>
  </div>

  <div class="wave-formula-panel" id="waveFormulaPanel" hidden="">
    <div class="wave-formula" aria-live="polite">
      <span class="wave-formula-base">y(t)</span>
      <span class="wave-formula-equals">=</span>
      <span class="wave-formula-amplitude" id="waveFormulaAmplitude">95</span>
      <span class="wave-formula-times">×</span>
      <span class="wave-formula-base">sin</span>
      <span class="wave-formula-paren">(</span>
      <span class="wave-formula-frequency">2π × <span id="waveFormulaFrequency">3.0</span> × t</span>
      <span class="wave-formula-phase"> + <span id="waveFormulaPhase">0°</span></span>
      <span class="wave-formula-paren">)</span>
    </div>
  </div>

  <svg id="waveSvg" viewBox="0 0 1000 430" role="img" aria-labelledby="waveTitle waveDescription">
    <title id="waveTitle">Animated sine wave</title>
    <desc id="waveDescription">
      An animated sine wave demonstrating amplitude, frequency and phase.
    </desc>

    <defs>
      <linearGradient id="waveGradient" x1="0%" y1="0%" x2="100%" y2="0%">
        <stop offset="0%" stop-color="#2563eb" />
        <stop offset="50%" stop-color="#7c3aed" />
        <stop offset="100%" stop-color="#db2777" />
      </linearGradient>

      <filter id="waveGlow" x="-20%" y="-40%" width="140%" height="180%">
        <feGaussianBlur stdDeviation="6" result="blur" />
        <feMerge>
          <feMergeNode in="blur" />
          <feMergeNode in="SourceGraphic" />
        </feMerge>
      </filter>

      <clipPath id="cycleHighlightClip">
        <rect id="cycleHighlightRect" x="390" y="65" width="230" height="285" />
      </clipPath>

      <clipPath id="plotClip">
        <rect x="80" y="65" width="860" height="285" />
      </clipPath>

      <marker id="arrowHead" markerWidth="8" markerHeight="8" refX="4" refY="4" orient="auto">
        <path d="M 0 0 L 8 4 L 0 8 z" fill="#64748b" />
      </marker>

      <marker id="orangeArrowHead" markerWidth="8" markerHeight="8" refX="4" refY="4" orient="auto">
        <path d="M 0 0 L 8 4 L 0 8 z" fill="#f59e0b" />
      </marker>
    </defs>

    <g class="wave-grid">
      <line x1="80" y1="80" x2="940" y2="80" />
      <line x1="80" y1="145" x2="940" y2="145" />
      <line x1="80" y1="210" x2="940" y2="210" />
      <line x1="80" y1="275" x2="940" y2="275" />
      <line x1="80" y1="340" x2="940" y2="340" />

      <line x1="80" y1="80" x2="80" y2="340" />
      <line x1="200" y1="80" x2="200" y2="340" />
      <line x1="320" y1="80" x2="320" y2="340" />
      <line x1="440" y1="80" x2="440" y2="340" />
      <line x1="560" y1="80" x2="560" y2="340" />
      <line x1="680" y1="80" x2="680" y2="340" />
      <line x1="800" y1="80" x2="800" y2="340" />
      <line x1="940" y1="80" x2="940" y2="340" />
    </g>

    <line class="wave-axis" x1="80" y1="210" x2="950" y2="210" />
    <line class="wave-axis" x1="80" y1="350" x2="80" y2="65" />

    <text id="timeAxisLabel" class="axis-label time-axis-label" x="905" y="238">Time</text>
    <text class="axis-label amplitude-axis-label" x="22" y="82">Amplitude</text>

    <path id="wavePathGlow" class="wave-path wave-path-glow" d="" />
    <path id="wavePath" class="wave-path" d="" />
    <path id="waveCycleHighlight" class="wave-path wave-cycle-highlight" clip-path="url(#cycleHighlightClip)" d="" />

    <g clip-path="url(#plotClip)">
      <g id="amplitudeGroupA">
        <line id="amplitudeLineA" class="measurement-line amplitude-marker-line" x1="0" y1="210" x2="0" y2="100" marker-start="url(#arrowHead)" marker-end="url(#arrowHead)" />
        <text id="amplitudeLabelA" class="measurement-label amplitude-marker-label" x="0" y="132" text-anchor="middle">Amplitude</text>
      </g>
      <g id="amplitudeGroupB">
        <line id="amplitudeLineB" class="measurement-line amplitude-marker-line" x1="0" y1="210" x2="0" y2="100" marker-start="url(#arrowHead)" marker-end="url(#arrowHead)" />
        <text id="amplitudeLabelB" class="measurement-label amplitude-marker-label" x="0" y="132" text-anchor="middle">Amplitude</text>
      </g>
    </g>

    <line id="frequencyMarker" class="measurement-line cycle-measurement-line" x1="390" y1="365" x2="620" y2="365" marker-start="url(#orangeArrowHead)" marker-end="url(#orangeArrowHead)" />
    <text id="frequencyLabel" class="measurement-label cycle-measurement-label" x="465" y="400">
      One cycle
    </text>

    <circle id="phasePoint" class="phase-point" cx="150" cy="210" r="7" />
    <text id="phaseLabel" class="measurement-label" x="105" y="55">
      Phase
    </text>
    <line id="phaseLine" class="phase-guide" x1="150" y1="65" x2="150" y2="203" />
  </svg>

  <div class="wave-controls">
    <label>
      <span>Amplitude</span>
      <input id="amplitudeControl" type="range" min="35" max="125" value="95" />
      <output id="amplitudeValue">95</output>
    </label>

    <label>
      <span>Frequency</span>
      <input id="frequencyControl" type="range" min="1" max="6" step="0.1" value="3" />
      <output id="frequencyValue">3.0</output>
    </label>

    <label>
      <span>Phase</span>
      <input id="phaseControl" type="range" min="0" max="360" value="0" />
      <output id="phaseValue">0°</output>
    </label>
  </div>
</div>

<style>
  .wave-lab {
    --wave-bg: #ffffff;
    --wave-text: #0f172a;
    --wave-text-muted: #64748b;
    --wave-border: #dbe4f0;
    --wave-panel: rgba(248, 250, 252, 0.92);
    --wave-grid: #e8edf5;
    --wave-axis: #64748b;
    --wave-outline: #ffffff;
    --wave-button-bg: #0f172a;
    --wave-button-text: #ffffff;
    --wave-shadow: 0 20px 60px rgba(15, 23, 42, 0.10);
    --wave-note-bg: linear-gradient(135deg, rgba(37, 99, 235, 0.08), rgba(124, 58, 237, 0.08));
    --wave-note-border: rgba(99, 102, 241, 0.18);
    --wave-accent: #7c3aed;
    width: min(100%, 1100px);
    margin: 2rem auto;
    padding: 1.25rem;
    overflow: hidden;
    color: var(--wave-text);
    background:
      radial-gradient(circle at top right, rgba(124, 58, 237, 0.10), transparent 32%),
      radial-gradient(circle at bottom left, rgba(37, 99, 235, 0.10), transparent 35%),
      var(--wave-bg);
    border: 1px solid var(--wave-border);
    border-radius: 22px;
    box-shadow: var(--wave-shadow);
    font-family: var(--font-body, Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);
    box-sizing: border-box;
  }

  .wave-lab[data-wave-theme="dark"] {
    --wave-bg: #09111d;
    --wave-text: #e4edf8;
    --wave-text-muted: #8ea2bf;
    --wave-border: rgba(129, 148, 179, 0.22);
    --wave-panel: rgba(13, 20, 33, 0.92);
    --wave-grid: rgba(146, 165, 192, 0.16);
    --wave-axis: #8fa2c1;
    --wave-outline: #09111d;
    --wave-button-bg: #e4edf8;
    --wave-button-text: #08111c;
    --wave-shadow: 0 22px 62px rgba(0, 0, 0, 0.32);
    --wave-note-bg: linear-gradient(135deg, rgba(37, 99, 235, 0.16), rgba(124, 58, 237, 0.16));
    --wave-note-border: rgba(129, 148, 179, 0.22);
    --wave-accent: #b79cff;
  }

  .wave-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.75rem;
  }

  .wave-header h3 {
    margin: 0;
    font-size: clamp(1.25rem, 2.5vw, 2rem);
    letter-spacing: -0.03em;
  }

  .wave-header p {
    margin: 0.35rem 0 0;
    color: var(--wave-text-muted);
  }

  .wave-header button {
    padding: 0.65rem 1rem;
    color: var(--wave-button-text);
    background: var(--wave-button-bg);
    border: 0;
    border-radius: 999px;
    cursor: pointer;
    font: inherit;
    font-weight: 600;
  }

  .wave-lab-note {
    margin-bottom: 0.9rem;
    padding: 0.9rem 1rem;
    color: var(--wave-text-muted);
    background: var(--wave-note-bg);
    border: 1px solid var(--wave-note-border);
    border-radius: 14px;
    line-height: 1.65;
  }

  .wave-lab-note strong {
    color: var(--wave-text);
  }

  .wave-lab-toolbar {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    margin-bottom: 0.55rem;
  }

  .wave-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.65rem;
    font-weight: 600;
    color: var(--wave-text);
    cursor: pointer;
    user-select: none;
  }

  .wave-toggle input {
    accent-color: var(--wave-accent);
  }

  .wave-formula-panel {
    margin-bottom: 0.75rem;
    padding: 0.9rem 1rem;
    background: var(--wave-panel);
    border: 1px solid var(--wave-border);
    border-radius: 14px;
  }

  .wave-formula {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    align-items: center;
    font-family: var(--font-mono, 'JetBrains Mono', monospace);
    font-size: 0.95rem;
    color: var(--wave-text);
  }

  .wave-formula-amplitude {
    color: #2563eb;
    font-weight: 700;
  }

  .wave-formula-frequency {
    color: #7c3aed;
    font-weight: 700;
  }

  .wave-formula-phase {
    color: #db2777;
    font-weight: 700;
  }

  #waveSvg {
    display: block;
    width: 100%;
    height: auto;
  }

  .wave-grid line {
    stroke: var(--wave-grid);
    stroke-width: 1;
  }

  .wave-axis {
    stroke: var(--wave-axis);
    stroke-width: 2;
  }

  .axis-label,
  .measurement-label {
    fill: var(--wave-axis);
    font-size: 20px;
    font-weight: 600;
  }

  .amplitude-marker-label,
  .amplitude-axis-label {
    paint-order: stroke fill;
    stroke: var(--wave-outline);
    stroke-width: 9px;
    stroke-linejoin: round;
  }

  .time-axis-label {
    paint-order: stroke fill;
    stroke: var(--wave-outline);
    stroke-width: 8px;
    stroke-linejoin: round;
    pointer-events: none;
  }

  .wave-path {
    fill: none;
    stroke: url("#waveGradient");
    stroke-width: 7;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  .wave-path-glow {
    opacity: 0.32;
    stroke-width: 18;
    filter: url("#waveGlow");
  }

  .wave-cycle-highlight {
    stroke: #f59e0b;
    stroke-width: 9;
  }

  .cycle-measurement-line {
    stroke: #f59e0b;
    marker-start: url("#orangeArrowHead");
    marker-end: url("#orangeArrowHead");
  }

  #frequencyMarker {
    stroke: #f59e0b;
  }

  .cycle-measurement-label {
    fill: #f59e0b;
  }

  .measurement-line {
    stroke: var(--wave-axis);
    stroke-width: 2;
    stroke-dasharray: 7 7;
  }

  .phase-guide {
    stroke: #db2777;
    stroke-width: 2;
    stroke-dasharray: 6 6;
  }

  .phase-point {
    fill: var(--wave-bg);
    stroke: #db2777;
    stroke-width: 5;
  }

  .wave-controls {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1rem;
    margin-top: 0.5rem;
  }

  .wave-controls label {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0.65rem;
    padding: 0.9rem;
    background: var(--wave-panel);
    border: 1px solid var(--wave-border);
    border-radius: 14px;
  }

  .wave-controls span {
    font-weight: 700;
  }

  .wave-controls input {
    grid-column: 1 / -1;
    width: 100%;
    accent-color: var(--wave-accent);
  }

  .wave-controls output {
    color: var(--wave-accent);
    font-weight: 800;
  }

  @media (max-width: 760px) {
    .wave-header {
      align-items: flex-start;
      flex-direction: column;
    }

    .wave-controls {
      grid-template-columns: 1fr;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .wave-header button {
      display: none;
    }
  }
</style>

<script>
  (() => {
    "use strict";

    const root = document.getElementById("waveLab");
    if (!root) return;

    const svgStartX = 80;
    const svgEndX = 940;
    const centerY = 210;

    const wavePath = document.getElementById("wavePath");
    const wavePathGlow = document.getElementById("wavePathGlow");
    const waveCycleHighlight = document.getElementById("waveCycleHighlight");
    const cycleHighlightRect = document.getElementById("cycleHighlightRect");
    const amplitudeControl = document.getElementById("amplitudeControl");
    const frequencyControl = document.getElementById("frequencyControl");
    const phaseControl = document.getElementById("phaseControl");
    const amplitudeValue = document.getElementById("amplitudeValue");
    const frequencyValue = document.getElementById("frequencyValue");
    const phaseValue = document.getElementById("phaseValue");
    const pauseButton = document.getElementById("wavePauseButton");
    const formulaToggle = document.getElementById("waveFormulaToggle");
    const formulaPanel = document.getElementById("waveFormulaPanel");
    const formulaAmplitude = document.getElementById("waveFormulaAmplitude");
    const formulaFrequency = document.getElementById("waveFormulaFrequency");
    const formulaPhase = document.getElementById("waveFormulaPhase");
    const timeAxisLabel = document.getElementById("timeAxisLabel");
    timeAxisLabel.parentNode.appendChild(timeAxisLabel);

    const amplitudeGroupA = document.getElementById("amplitudeGroupA");
    const amplitudeGroupB = document.getElementById("amplitudeGroupB");
    const amplitudeLineA = document.getElementById("amplitudeLineA");
    const amplitudeLineB = document.getElementById("amplitudeLineB");
    const amplitudeLabelA = document.getElementById("amplitudeLabelA");
    const amplitudeLabelB = document.getElementById("amplitudeLabelB");
    const frequencyMarker = document.getElementById("frequencyMarker");
    const frequencyLabel = document.getElementById("frequencyLabel");
    const phasePoint = document.getElementById("phasePoint");
    const phaseLine = document.getElementById("phaseLine");

    let animationPhase = 0;
    let isPaused = false;
    let previousTimestamp = performance.now();
    let activeCrestK = null;
    let portalTargetK = null;
    let lastFrequency = null;
    let lastManualPhaseRadians = null;

    function resolveWaveTheme() {
      let storedTheme = null;
      try {
        storedTheme = localStorage.getItem("theme");
      } catch (error) {
        storedTheme = null;
      }

      const htmlTheme = document.documentElement.getAttribute("data-theme");

      if (htmlTheme === "light" || storedTheme === "light") {
        return "light";
      }

      if (htmlTheme === "dark" || storedTheme === "dark") {
        return "dark";
      }

      return "light";
    }

    function applyWaveTheme() {
      root.setAttribute("data-wave-theme", resolveWaveTheme());
    }

    applyWaveTheme();

    new MutationObserver(applyWaveTheme).observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["data-theme"]
    });

    function createPortalWavePath(amplitude, frequency, manualPhaseRadians, travelX) {
      const width = svgEndX - svgStartX;
      const steps = 900;
      const points = [];

      for (let index = 0; index <= steps; index += 1) {
        const progress = index / steps;
        const x = svgStartX + progress * width;
        const movingProgress = progress + travelX / width;
        const angle = movingProgress * Math.PI * 2 * frequency + manualPhaseRadians;
        const y = centerY - amplitude * Math.sin(angle);

        points.push(`${index === 0 ? "M" : "L"} ${x.toFixed(2)} ${y.toFixed(2)}`);
      }

      return points.join(" ");
    }

    function clamp(value, minimum, maximum) {
      return Math.max(minimum, Math.min(maximum, value));
    }

    function smoothstep(edge0, edge1, value) {
      if (edge0 === edge1) {
        return value < edge0 ? 0 : 1;
      }

      const progress = clamp((value - edge0) / (edge1 - edge0), 0, 1);
      return progress * progress * (3 - 2 * progress);
    }

    function setMarkerLine(line, x, topY, opacity) {
      line.setAttribute("x1", x.toFixed(2));
      line.setAttribute("x2", x.toFixed(2));
      line.setAttribute("y1", centerY);
      line.setAttribute("y2", topY);
      line.style.opacity = String(opacity);
    }

    function setMarkerLabel(label, x, y, opacity) {
      label.setAttribute("x", x.toFixed(2));
      label.setAttribute("y", y.toFixed(2));
      label.style.opacity = String(opacity);
    }

    function updateAnnotations(amplitude, frequency, manualPhaseRadians, travelX) {
      const width = svgEndX - svgStartX;
      const cycleWidth = width / frequency;
      const tau = Math.PI * 2;
      const phaseAtStart = travelX / width * tau * frequency + manualPhaseRadians;

      function crestX(k) {
        const progress = (Math.PI / 2 + k * tau - phaseAtStart) / (tau * frequency);
        return svgStartX + progress * width;
      }

      const controlsChanged =
        activeCrestK === null ||
        lastFrequency !== frequency ||
        lastManualPhaseRadians !== manualPhaseRadians;

      if (controlsChanged) {
        const preferredX = 230;
        activeCrestK = Math.round(
          (phaseAtStart + ((preferredX - svgStartX) / width) * tau * frequency - Math.PI / 2) / tau
        );
        portalTargetK = null;
        lastFrequency = frequency;
        lastManualPhaseRadians = manualPhaseRadians;
      }

      let activeX = crestX(activeCrestK);
      const amplitudeTop = centerY - amplitude;
      const labelY = centerY - amplitude / 2 + 7;

      let labelHalfWidth = 58;
      try {
        labelHalfWidth = Math.max(labelHalfWidth, amplitudeLabelA.getComputedTextLength() / 2 + 6);
      } catch (error) {
        labelHalfWidth = 58;
      }

      const portalStartX = svgStartX + labelHalfWidth + 42;
      const portalEndX = svgStartX - labelHalfWidth - 10;
      const entryStartX = svgEndX + labelHalfWidth + 4;

      if (portalTargetK === null && activeX <= portalStartX) {
        const idealStride = (entryStartX - portalStartX) / cycleWidth;
        const stride = Math.max(1, Math.round(idealStride));
        portalTargetK = activeCrestK + stride;
      }

      amplitudeGroupA.removeAttribute("transform");
      amplitudeGroupB.removeAttribute("transform");

      if (portalTargetK !== null) {
        const targetX = crestX(portalTargetK);
        const rawProgress = clamp((portalStartX - activeX) / (portalStartX - portalEndX), 0, 1);
        const wordProgress = smoothstep(0.04, 1, rawProgress);
        const incomingLabelX = entryStartX + (targetX - entryStartX) * wordProgress;

        setMarkerLabel(amplitudeLabelA, activeX, labelY, 1);
        setMarkerLabel(amplitudeLabelB, incomingLabelX, labelY, 1);

        const oldLineOpacity = 1 - smoothstep(0.28, 0.78, rawProgress);
        const newLineOpacity = smoothstep(0.84, 1, rawProgress);
        setMarkerLine(amplitudeLineA, activeX, amplitudeTop, oldLineOpacity);
        setMarkerLine(amplitudeLineB, targetX, amplitudeTop, newLineOpacity);

        if (rawProgress >= 0.999) {
          activeCrestK = portalTargetK;
          portalTargetK = null;
        }
      } else {
        setMarkerLine(amplitudeLineA, activeX, amplitudeTop, 1);
        setMarkerLabel(amplitudeLabelA, activeX, labelY, 1);
        setMarkerLine(amplitudeLineB, activeX, amplitudeTop, 0);
        setMarkerLabel(amplitudeLabelB, entryStartX, labelY, 0);
      }

      const cycleStart = 390;
      const cycleEnd = Math.min(cycleStart + cycleWidth, svgEndX - 10);

      frequencyMarker.setAttribute("x1", cycleStart);
      frequencyMarker.setAttribute("x2", cycleEnd);
      frequencyLabel.setAttribute("x", (cycleStart + cycleEnd) / 2);
      frequencyLabel.setAttribute("text-anchor", "middle");

      cycleHighlightRect.setAttribute("x", cycleStart);
      cycleHighlightRect.setAttribute("width", Math.max(0, cycleEnd - cycleStart));

      const phaseX = 150;
      const phaseProgress = (phaseX - svgStartX) / width;
      const phaseY = centerY - amplitude * Math.sin(
        (phaseProgress + travelX / width) * tau * frequency + manualPhaseRadians
      );

      phasePoint.setAttribute("cx", phaseX);
      phasePoint.setAttribute("cy", phaseY);
      phaseLine.setAttribute("x1", phaseX);
      phaseLine.setAttribute("x2", phaseX);
      phaseLine.setAttribute("y2", phaseY - 8);
    }

    function updateFormula(amplitude, frequency, manualPhaseDegrees) {
      formulaAmplitude.textContent = String(amplitude);
      formulaFrequency.textContent = frequency.toFixed(1);
      formulaPhase.textContent = `${manualPhaseDegrees}°`;
    }

    function renderWave(timestamp) {
      const elapsedSeconds = Math.min((timestamp - previousTimestamp) / 1000, 0.1);

      previousTimestamp = timestamp;

      if (!isPaused && !window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
        animationPhase += elapsedSeconds * 1.8;
      }

      const amplitude = Number(amplitudeControl.value);
      const frequency = Number(frequencyControl.value);
      const manualPhaseDegrees = Number(phaseControl.value);
      const width = svgEndX - svgStartX;
      const manualPhaseRadians = manualPhaseDegrees * Math.PI / 180;
      const cycleWidth = width / frequency;
      const travelX = animationPhase / (Math.PI * 2) * cycleWidth;

      const pathData = createPortalWavePath(amplitude, frequency, manualPhaseRadians, travelX);

      wavePath.setAttribute("d", pathData);
      wavePathGlow.setAttribute("d", pathData);
      waveCycleHighlight.setAttribute("d", pathData);
      updateAnnotations(amplitude, frequency, manualPhaseRadians, travelX);
      updateFormula(amplitude, frequency, manualPhaseDegrees);

      amplitudeValue.value = String(amplitude);
      frequencyValue.value = frequency.toFixed(1);
      phaseValue.value = `${manualPhaseDegrees}°`;

      window.requestAnimationFrame(renderWave);
    }

    pauseButton.addEventListener("click", () => {
      isPaused = !isPaused;
      pauseButton.textContent = isPaused ? "Play" : "Pause";
    });

    formulaToggle.addEventListener("change", () => {
      formulaPanel.hidden = !formulaToggle.checked;
    });

    window.requestAnimationFrame(renderWave);
  })();
</script>

<h4 id="how-does-a-wave-carry-information">How Does a Wave Carry Information?</h4>

<p>The existence of a wave is not enough to create communication. A sine wave with fixed frequency, amplitude, and phase can transfer energy from one point to another, but as long as it remains identical over time, the receiver has no way to infer new information from it. Once the receiver identifies the waveform parameters, every additional cycle is expected to be the same as the previous one. In communication terms, the signal adds nothing.</p>

<p>Information is created when the transmitter chooses between possible states and the receiver can distinguish those states. That choice can be expressed as changes in frequency, amplitude, phase, or timing. For example, if the transmitter uses one frequency to represent 0 and another frequency to represent 1, the receiver can analyze the signal over each time interval and reconstruct the bit sequence based on the detected frequency. The same principle applies to amplitude, where a lower level can represent one value and a higher level another, or to phase, where multiple phase states can represent multiple bits per symbol.</p>

<p>From this, it follows that the wave itself is not the information. The information is in the transmitter’s controlled choice to change a specific wave property, and in the receiver’s ability to detect that change despite noise, distortion, and channel constraints. The mapping process between digital information and physical signal states is called modulation.</p>

<p>The amount of information represented by each symbol depends on how many states the receiver can reliably distinguish. With two states, each symbol carries one bit. With four distinguishable states, each symbol carries two bits. Sixteen states carry four bits. In general, if there are $M$ possible states, the number of bits per symbol is log2(M).</p>

<p>However, increasing the number of states does not provide information for free. As the physical distance between states becomes smaller, it becomes harder for the receiver to distinguish them after the signal passes through a noisy or distorted channel. For example, it is relatively easy to detect whether a specific frequency is present or absent, but harder to differentiate sixteen nearby amplitude levels after an audio codec has changed gain, compressed dynamic range, or introduced distortion.</p>

<p>That is why modulation selection is not just a question of how many bits can be packed into each symbol. It requires balancing data rate against state spacing, channel quality, and the receiver’s measurement capability. An efficient communication system is not the one that encodes the most bits per symbol, but the one that transfers the largest amount of information that can still be recovered reliably under real channel conditions.</p>

<h5 id="codec">Codec</h5>

<p>A codec is a mechanism that encodes and decodes a digital signal, typically to make it suitable for transmission or storage. The term codec comes from the combination of encoder and decoder. On the transmitter side, the codec receives a signal, processes it, and produces a compressed or channel-adapted representation. On the receiver side, it performs the reverse process and attempts to reconstruct the original signal.</p>

<p>In audio, a codec may reduce information by removing or approximating details considered less important to human hearing. A lossless codec allows exact reconstruction of the original information, while a lossy codec sacrifices some information to reduce required data rate.</p>

<p>From the perspective of a covert communication system, the codec is not just an audio component. It is part of the channel. It can attenuate certain frequencies, alter amplitude, reduce phase stability, smear short-time changes, or remove spectral components it considers insignificant. As a result, a precisely generated transmitter-side signal is not necessarily the same signal that reaches the receiver.</p>

<h5 id="noise">Noise</h5>

<p>Noise is any unwanted change added to the signal, or any distortion applied to it during transmission, reception, or processing. Noise makes it harder for the receiver to distinguish the states that the transmitter intended to generate.</p>

<p>Noise is not limited to audible background sound. It can come from many sources, including hardware limitations, quantization error, audio compression, mixing with other sounds, operating system processing, packet loss, gain variation, or timing inaccuracy.</p>

<p>For example, if one frequency represents 0 and another represents 1, noise can shift the measured frequency, attenuate it, or add energy at a nearby frequency. If the states are too close, the receiver may decode the wrong value.</p>

<p>Signal quality is often measured using signal-to-noise ratio (SNR), which compares desired signal power to noise power. The higher the ratio, the easier it is to distinguish symbols and recover information reliably.</p>

<figure class="figure-centered">
  <img src="/assets/images/citrix-audio/image2.png" alt="Wave communication illustration for the current section" />
  <figcaption>Figure 2 - Visual support for the wave communication model</figcaption>
</figure>

<h4 id="time-to-stop-guessing">Time to Stop Guessing</h4>

<p>One of the most common mistakes in engineering projects in general, and in Offensive Security research in particular, is the tendency to start building too early. It is easy to pick a modulation method that sounds promising, build a proof of concept, and start optimizing. The problem with this approach is that it assumes the channel will behave the way we expect it to.</p>

<p>In the case of an audio channel, that is a risky assumption.</p>

<p>By this point, it was already clear that the Citrix audio channel could carry information. The question was no longer whether it could be used, but how it should be used. In other words, it was time to stop guessing and start measuring.</p>

<p>This was also the point where I realized my goal was not to find the “best” modulation method, but to understand what information the channel could preserve across the full transmission path. The difference between those two approaches is substantial. The first starts from a solution and tries to fit it into the channel. The second starts from the channel and only then selects the appropriate solution.</p>

<p>At this stage, I began treating the audio path like any other communication system. If it is a communication channel, it can be characterized, measured, and bounded. So before making any engineering decision, I needed answers to a set of basic questions:</p>

<ul>
  <li>Which frequencies pass reliably through the full communication chain?</li>
  <li>Is signal amplitude preserved, or does it drift along the path?</li>
  <li>Is phase preserved consistently, or does processing alter it?</li>
  <li>Are there frequency bands where the codec significantly degrades signal quality?</li>
  <li>What is the shortest symbol duration that can be transmitted before the codec smears it in time?</li>
  <li>Are the stereo channels preserved as independent channels, or do they mix during compression?</li>
  <li>In practice, what data rate can be achieved without sacrificing reliability?</li>
</ul>

<p>These were not theoretical questions. Each one led to an experiment, and each experiment influenced the final system architecture.</p>

<p>At this point, I also stopped looking at Citrix as a product. For me, Citrix was just one stage in a long processing chain. The signal did not travel directly from speaker to receiver. Along the way, it passed through the operating system, the Citrix audio subsystem, the compression codec, the transport protocol, the endpoint device, the local audio subsystem, and finally the recording stage through WASAPI. Every one of those components could alter, attenuate, or discard information from the signal.</p>

<p>In other words, the real challenge was not producing a sound wave. The real challenge was understanding which wave properties survive the entire path.</p>

<p>That became the starting point of the experiment series.</p>

<p>Instead of measuring only raw data rate, each experiment was designed to answer one question: can the channel preserve a specific signal property? The study examined frequency regions that survive codec passage consistently, long-term amplitude stability, phase behavior after channel traversal, the number of carriers that can be used in parallel, the effect of symbol duration on error rate, and the impact of switching between mono and stereo on throughput and reliability. Only after each of these questions had an evidence-based answer could design decisions begin.</p>

<p>In hindsight, most of the research time was not spent writing code. It was spent measuring. Every measurement eliminated several options, but also narrowed the solution space and clarified which decisions were truly compatible with the channel in front of us.</p>

<p>Only after the channel was characterized sufficiently could the communication system itself be designed.</p>

<div class="attacker-flow" id="attackerFlow" dir="ltr" data-flow-theme="light">
  <svg viewBox="0 0 1000 1280" role="img" aria-labelledby="attackerFlowTitle attackerFlowDescription">
    <title id="attackerFlowTitle">
      Attacker Channel-First Decision Flow
    </title>

    <desc id="attackerFlowDescription">
      A flowchart showing how an attacker moves from an objective,
      through channel discovery, characterization and measurement,
      to system design and implementation.
    </desc>

    <defs>
      <filter id="attackerFlowCardShadow" x="-20%" y="-20%" width="140%" height="160%">
        <feDropShadow dx="0" dy="5" stdDeviation="7" flood-color="#253047" flood-opacity="0.12" />
      </filter>

      <marker id="attackerFlowArrowHead" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="8" markerHeight="8" orient="auto">
        <path d="M 0 0 L 10 5 L 0 10 z" fill="var(--flow-arrow, #596579)" />
      </marker>

      <pattern id="attackerFlowPaperGrid" width="24" height="24" patternUnits="userSpaceOnUse">
        <path d="M 24 0 L 0 0 0 24" fill="none" stroke="var(--flow-grid, #233047)" stroke-width="1" opacity="0.035" />
      </pattern>

      <linearGradient id="attackerFlowFinalGradient" x1="0%" y1="0%" x2="100%" y2="100%">
        <stop offset="0%" stop-color="var(--flow-final-a, #27354d)" />
        <stop offset="100%" stop-color="var(--flow-final-b, #3e506f)" />
      </linearGradient>
    </defs>

    <rect x="0" y="0" width="1000" height="1280" rx="26" fill="var(--flow-bg, #f4f0e8)" />

    <rect x="0" y="0" width="1000" height="1280" rx="26" fill="url(#attackerFlowPaperGrid)" />

    <text x="500" y="65" text-anchor="middle" class="flow-eyebrow">
      ATTACKER THINKING
    </text>

    <text x="500" y="112" text-anchor="middle" class="flow-title">
      The Channel Comes Before the Code
    </text>

    <text x="500" y="148" text-anchor="middle" class="flow-subtitle">
      Start with the objective. Let the channel shape the implementation.
    </text>

    <rect x="250" y="190" width="500" height="110" rx="16" class="flow-card card-yellow" />

    <text x="285" y="226" class="step-number">01</text>
    <text x="340" y="228" class="step-title">Define the objective</text>

    <text x="285" y="262" class="step-copy">
      Move information across a controlled boundary.
    </text>

    <text x="285" y="285" class="step-copy">
      Do not choose the technique yet.
    </text>

    <line x1="500" y1="300" x2="500" y2="352" class="flow-line" marker-end="url(#attackerFlowArrowHead)" />

    <polygon points="500,365 700,465 500,565 300,465" class="decision" />

    <text x="500" y="450" text-anchor="middle" class="decision-title">
      Is there a channel?
    </text>

    <text x="500" y="482" text-anchor="middle" class="decision-copy">
      What still crosses the boundary?
    </text>

    <line x1="300" y1="465" x2="190" y2="465" class="flow-line" marker-end="url(#attackerFlowArrowHead)" />

    <text x="245" y="442" text-anchor="middle" class="branch-no">
      NO
    </text>

    <rect x="45" y="410" width="145" height="110" rx="14" class="flow-card card-red" />

    <text x="117" y="448" text-anchor="middle" class="side-title">
      Look again
    </text>

    <text x="117" y="478" text-anchor="middle" class="side-copy">
      Stop thinking
    </text>

    <text x="117" y="500" text-anchor="middle" class="side-copy">
      in features
    </text>

    <path d="M 117 520
         L 117 610
         L 250 610
         L 250 540
         L 336 540" class="return-line" marker-end="url(#attackerFlowArrowHead)" />

    <text x="182" y="590" text-anchor="middle" class="loop-label">
      Reframe the problem
    </text>

    <line x1="500" y1="565" x2="500" y2="625" class="flow-line" marker-end="url(#attackerFlowArrowHead)" />

    <text x="530" y="601" class="branch-yes">
      YES
    </text>

    <rect x="230" y="635" width="540" height="120" rx="16" class="flow-card card-blue" />

    <text x="265" y="674" class="step-number">02</text>
    <text x="320" y="676" class="step-title">
      Characterize the channel
    </text>

    <text x="265" y="711" class="step-copy">
      Identify what the channel preserves,
    </text>

    <text x="265" y="735" class="step-copy">
      distorts or removes.
    </text>

    <line x1="500" y1="755" x2="500" y2="807" class="flow-line" marker-end="url(#attackerFlowArrowHead)" />

    <rect x="230" y="817" width="540" height="132" rx="16" class="flow-card card-green" />

    <text x="265" y="856" class="step-number">03</text>
    <text x="320" y="858" class="step-title">
      Measure instead of assuming
    </text>

    <text x="265" y="894" class="step-copy">
      Frequency · amplitude · phase · noise
    </text>

    <text x="265" y="919" class="step-copy">
      Symbol duration · codec behaviour · stereo
    </text>

    <line x1="500" y1="949" x2="500" y2="1001" class="flow-line" marker-end="url(#attackerFlowArrowHead)" />

    <rect x="230" y="1011" width="540" height="120" rx="16" class="flow-card card-white" />

    <text x="265" y="1050" class="step-number">04</text>
    <text x="320" y="1052" class="step-title">
      Design around the constraints
    </text>

    <text x="265" y="1088" class="step-copy">
      Choose modulation, framing, error correction
    </text>

    <text x="265" y="1112" class="step-copy">
      and recovery based on measured behaviour.
    </text>

    <line x1="500" y1="1131" x2="500" y2="1183" class="flow-line" marker-end="url(#attackerFlowArrowHead)" />

    <rect x="300" y="1193" width="400" height="72" rx="36" class="final-card" />

    <text x="500" y="1223" text-anchor="middle" class="final-small">
      05 · LAST
    </text>

    <text x="500" y="1251" text-anchor="middle" class="final-title">
      Write the code
    </text>
  </svg>
</div>

<style>
  .attacker-flow,
  .attacker-flow * {
    box-sizing: border-box;
  }

  .attacker-flow {
    --flow-bg: #f4f0e8;
    --flow-grid: #233047;
    --flow-border: #ddd5c8;
    --flow-shadow-a: rgba(32, 40, 56, 0.10);
    --flow-shadow-b: rgba(32, 40, 56, 0.05);
    --flow-arrow: #596579;
    --flow-eyebrow: #a65d44;
    --flow-title: #20293a;
    --flow-subtitle: #697182;
    --flow-card-text: #202938;
    --flow-copy: #515d6d;
    --flow-side-copy: #655d5b;
    --flow-loop: #846d66;
    --flow-decision: #2c3749;
    --flow-decision-stroke: #1c2432;
    --flow-decision-text: #fffdf7;
    --flow-decision-copy: #cfd5df;
    --flow-final-a: #27354d;
    --flow-final-b: #3e506f;
    --flow-final-small: #e4b77c;
    width: min(100%, 900px);
    margin: 2.5rem auto;
    overflow: hidden;
    background: var(--flow-bg);
    border: 1px solid var(--flow-border);
    border-radius: 24px;
    box-shadow:
      0 22px 60px var(--flow-shadow-a),
      0 2px 5px var(--flow-shadow-b);
  }

  .attacker-flow[data-flow-theme="dark"] {
    --flow-bg: #0b1118;
    --flow-grid: #d7e3f4;
    --flow-border: rgba(129, 148, 179, 0.24);
    --flow-shadow-a: rgba(0, 0, 0, 0.28);
    --flow-shadow-b: rgba(0, 0, 0, 0.18);
    --flow-arrow: #90a3bf;
    --flow-eyebrow: #e9a783;
    --flow-title: #f2f6fb;
    --flow-subtitle: #9fb0c8;
    --flow-card-text: #eaf0f8;
    --flow-copy: #bcc9db;
    --flow-side-copy: #d9c9c3;
    --flow-loop: #c0aba5;
    --flow-decision: #1a2330;
    --flow-decision-stroke: #42546f;
    --flow-decision-text: #ffffff;
    --flow-decision-copy: #b8c7db;
    --flow-final-a: #1d2b41;
    --flow-final-b: #2f476c;
    --flow-final-small: #f2c07d;
  }

  .attacker-flow svg {
    display: block;
    width: 100%;
    height: auto;
    font-family:
      var(--font-body, Inter),
      ui-sans-serif,
      system-ui,
      -apple-system,
      BlinkMacSystemFont,
      "Segoe UI",
      sans-serif;
  }

  .flow-eyebrow {
    fill: var(--flow-eyebrow);
    font-size: 13px;
    font-weight: 850;
    letter-spacing: 2.4px;
  }

  .flow-title {
    fill: var(--flow-title);
    font-family: Georgia, "Times New Roman", serif;
    font-size: 34px;
    font-weight: 700;
  }

  .flow-subtitle {
    fill: var(--flow-subtitle);
    font-size: 15px;
  }

  .flow-card {
    stroke-width: 1.7;
    filter: url("#attackerFlowCardShadow");
  }

  .card-yellow {
    fill: #fff1bd;
    stroke: #d1b45c;
  }

  .card-red {
    fill: #f7ddd6;
    stroke: #cc8876;
  }

  .card-blue {
    fill: #dfe9f3;
    stroke: #88a2b8;
  }

  .card-green {
    fill: #dfe9dc;
    stroke: #8ca184;
  }

  .card-white {
    fill: #fffdf7;
    stroke: #aab0b8;
  }

  .attacker-flow[data-flow-theme="dark"] .card-yellow {
    fill: #4d4220;
    stroke: #cfb56a;
  }

  .attacker-flow[data-flow-theme="dark"] .card-red {
    fill: #4b2d2d;
    stroke: #cf8d83;
  }

  .attacker-flow[data-flow-theme="dark"] .card-blue {
    fill: #223244;
    stroke: #88a2b8;
  }

  .attacker-flow[data-flow-theme="dark"] .card-green {
    fill: #23342a;
    stroke: #8ca184;
  }

  .attacker-flow[data-flow-theme="dark"] .card-white {
    fill: #1a2028;
    stroke: #92a0b2;
  }

  .decision {
    fill: var(--flow-decision);
    stroke: var(--flow-decision-stroke);
    stroke-width: 2;
    filter: url("#attackerFlowCardShadow");
  }

  .decision-title {
    fill: var(--flow-decision-text);
    font-family: Georgia, "Times New Roman", serif;
    font-size: 24px;
    font-weight: 700;
  }

  .decision-copy {
    fill: var(--flow-decision-copy);
    font-size: 14px;
  }

  .step-number {
    fill: #956344;
    font-size: 13px;
    font-weight: 900;
    letter-spacing: 1.3px;
  }

  .attacker-flow[data-flow-theme="dark"] .step-number {
    fill: #e0b489;
  }

  .step-title {
    fill: var(--flow-card-text);
    font-family: Georgia, "Times New Roman", serif;
    font-size: 22px;
    font-weight: 700;
  }

  .step-copy {
    fill: var(--flow-copy);
    font-size: 15px;
  }

  .side-title {
    fill: var(--flow-card-text);
    font-family: Georgia, "Times New Roman", serif;
    font-size: 18px;
    font-weight: 700;
  }

  .side-copy {
    fill: var(--flow-side-copy);
    font-size: 13px;
  }

  .flow-line {
    fill: none;
    stroke: var(--flow-arrow);
    stroke-width: 2.5;
    stroke-linecap: round;
  }

  .return-line {
    fill: none;
    stroke: #9b685b;
    stroke-width: 2.2;
    stroke-dasharray: 7 7;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  .branch-no,
  .branch-yes {
    font-size: 12px;
    font-weight: 900;
    letter-spacing: 1.2px;
  }

  .branch-no {
    fill: #a24f42;
  }

  .branch-yes {
    fill: #4d7757;
  }

  .attacker-flow[data-flow-theme="dark"] .branch-no {
    fill: #e69b8f;
  }

  .attacker-flow[data-flow-theme="dark"] .branch-yes {
    fill: #9ec6a1;
  }

  .loop-label {
    fill: var(--flow-loop);
    font-size: 12px;
    font-style: italic;
  }

  .final-card {
    fill: url("#attackerFlowFinalGradient");
    stroke: #1d2738;
    stroke-width: 2;
    filter: url("#attackerFlowCardShadow");
  }

  .attacker-flow[data-flow-theme="dark"] .final-card {
    stroke: #4f6482;
  }

  .final-small {
    fill: var(--flow-final-small);
    font-size: 11px;
    font-weight: 900;
    letter-spacing: 1.6px;
  }

  .final-title {
    fill: #ffffff;
    font-family: Georgia, "Times New Roman", serif;
    font-size: 22px;
    font-weight: 700;
  }

  @media (max-width: 700px) {
    .attacker-flow {
      overflow-x: auto;
      border-radius: 16px;
    }

    .attacker-flow svg {
      width: 760px;
      max-width: none;
    }
  }
</style>

<script>
  (() => {
    const root = document.getElementById("attackerFlow");
    if (!root) return;

    function resolveFlowTheme() {
      let storedTheme = null;
      try {
        storedTheme = localStorage.getItem("theme");
      } catch (error) {
        storedTheme = null;
      }

      const htmlTheme = document.documentElement.getAttribute("data-theme");

      if (htmlTheme === "light" || storedTheme === "light") {
        return "light";
      }

      if (htmlTheme === "dark" || storedTheme === "dark") {
        return "dark";
      }

      return "light";
    }

    function applyFlowTheme() {
      root.setAttribute("data-flow-theme", resolveFlowTheme());
    }

    applyFlowTheme();

    new MutationObserver(applyFlowTheme).observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["data-theme"]
    });
  })();
</script>

<h2 id="and-then-everything-broke" class="post-h2-center post-h2-emphasis">And Then... Everything Broke</h2>

<p>After several days of reading, understanding the core concepts, and building a mental model of the channel, I felt I was finally ready to begin.</p>

<p>I had a fairly clear picture of how information can be represented on top of a wave, which wave properties can be changed, and which principles modern communication systems rely on. On paper, all that seemed left was to choose a modulation method, write a bit of code, and start transmitting data.</p>

<p>In practice, almost all of my initial assumptions turned out to be partial, and sometimes simply wrong.</p>

<p>In hindsight, this was the most important phase of the entire research.</p>

<p>The transition from theory to a working system did not happen through one brilliant idea or a new algorithm. It happened through a long series of experiments, measurements, and small decisions, each of which forced me to abandon a previous assumption and adopt a better way of thinking.</p>

<h3 id="more-complex-better">More complex = better</h3>

<p>That was probably the first assumption to break.</p>

<p>When you start reading about communication systems, it is very easy to be impressed by advanced modulation schemes. They can represent more bits per symbol, use bandwidth more efficiently, and reach much higher data rates. On paper, this looked like the obvious choice. In practice, the channel I worked with did not behave like a textbook channel at all.</p>

<figure class="figure-centered">
  <img src="/assets/images/citrix-audio/image3.png" alt="Experimental transition from theoretical assumptions to measured channel behavior" />
  <figcaption>Figure 3 - Theory breaks, measurement starts</figcaption>
</figure>

<p>As I went deeper, it became clear that the real question was not how much information can be represented per symbol, but how much of it survives the trip through the channel. At that stage, I still did not know which modulation would win. But it was already clear that the choice would not be based on how many bits could be packed into each symbol, but on how the channel actually behaves.</p>

<div class="post-fact-callout">
  <p class="post-fact-label">Fact 01</p>
  <p class="post-fact-text">The best modulation is not the one that carries the most information, but the one that survives the channel best.</p>
</div>

<h3 id="the-channel-decides-not-you">The channel decides. Not you.</h3>

<p>At first, I was convinced I could choose any modulation method I wanted, and all that would remain was to implement it. In practice, almost every decision was made for me:</p>

<ul>
  <li>The channel decided which frequencies survive.</li>
  <li>The codec decided which signal characteristics are preserved.</li>
  <li>The noise decided how many states can actually be distinguished.</li>
</ul>

<div class="post-fact-callout">
  <p class="post-fact-label">Fact 02</p>
  <p class="post-fact-text">I did not choose the architecture. The channel chose it for me.</p>
</div>

<figure class="figure-centered">
  <img src="/assets/images/citrix-audio/image4.png" alt="Channel-driven architecture constraints in practice" />
  <figcaption>Figure 4 - The channel dictates architecture choices</figcaption>
</figure>

<h3 id="measurement-beats-intuition">Measurement beats intuition</h3>

<p>At this stage, I had already understood something more important than any formula I had read. Almost every decision I tried to make from intuition turned out to be wrong.</p>

<ul>
  <li>More carriers did not necessarily increase the transfer rate.</li>
  <li>More states did not necessarily increase the number of bits that actually made it through.</li>
</ul>

<p>Even a modulation scheme that looked “inferior” in theory sometimes transferred more information, simply because it survived the channel better.</p>

<p>From that point on, I stopped asking, “What should work?” and started asking, “What do the measurements say?” That was probably the most significant shift in the entire research.</p>

<div class="post-fact-callout">
  <p class="post-fact-label">Fact 03</p>
  <p class="post-fact-text">Measurements are always better than intuition.</p>
</div>

<figure class="figure-centered">
  <img src="/assets/images/citrix-audio/image5.png" alt="Measurement-driven refinement of channel communication strategy" />
  <figcaption>Figure 5 - Measurement over intuition in channel design</figcaption>
</figure>

<p>Up to this point, the objective was to understand the channel. Only after reliable measurements started to accumulate was it possible to begin building the system itself. Almost ironically, the coding phase turned out to be relatively straightforward. The real challenge was understanding what the channel allows, and what it will never allow. Only then could all of those insights be translated into engineering decisions and a working communication system.</p>

<h2 id="vdi-bridge" class="post-h2-center post-h2-emphasis">VDI Bridge</h2>

<p>After a long period of learning, experiments, measurements, and course corrections, the result was no longer just a Proof of Concept that could push a single signal from one side to the other. It had become a full communication system.</p>

<p>VDI Bridge was not born as a finished idea. It evolved gradually, with each engineering decision serving as a response to a limitation exposed in the previous experiment. Compression was added to deal with the channel’s limited bandwidth. Error-correction mechanisms were added after it became clear that even a low error rate is enough to corrupt binary information. Later, interleaving was introduced after it became clear that errors were not random but appeared in short bursts. Even the shift between modulation methods was not driven by theoretical preference, but by how the Citrix audio channel and codec behaved in practice.</p>

<p>In the end, the system that was built included all the components you would expect in a modern communication system: compression, framing, synchronization, modulation, error correction, interleaving, validation, and recovery stages on the receiver side. Even though the physical medium was an audio channel, the design process was not fundamentally different from designing any other communication system.</p>

<p>Of course, the code is available here: <a href="https://github.com/danieloz147/VDI-Bridge/">https://github.com/danieloz147/VDI-Bridge/</a></p>

<figure class="figure-centered">
  <div style="position: relative; width: 100%; max-width: 960px; margin: 0 auto; padding-bottom: 56.25%; height: 0; overflow: hidden;">
    <iframe src="https://www.youtube.com/embed/HdL7QEHDsYY" title="VDI Bridge demonstration" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;" loading="lazy" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>
  </div>
  <figcaption>VDI Bridge demonstration video</figcaption>
</figure>

<figure class="vdi-bridge-diagram" id="vdiBridgeDiagram" dir="ltr" data-vdi-theme="dark">
  <div class="vdi-bridge-visual" aria-label="VDI Bridge communication flow">

    <button type="button" class="vdi-step" data-detail="source" aria-pressed="true">
      <span class="vdi-step-number">01</span>
      <span class="vdi-step-title">Raw data</span>
      <span class="vdi-step-caption">File, clipboard or control message</span>
    </button>

    <span class="vdi-arrow" aria-hidden="true">→</span>

    <button type="button" class="vdi-step" data-detail="prepare" aria-pressed="false">
      <span class="vdi-step-number">02</span>
      <span class="vdi-step-title">Prepare</span>
      <span class="vdi-step-caption">Compress and frame</span>
    </button>

    <span class="vdi-arrow" aria-hidden="true">→</span>

    <button type="button" class="vdi-step" data-detail="protect" aria-pressed="false">
      <span class="vdi-step-number">03</span>
      <span class="vdi-step-title">Protect</span>
      <span class="vdi-step-caption">FEC and interleaving</span>
    </button>

    <span class="vdi-arrow" aria-hidden="true">→</span>

    <button type="button" class="vdi-step" data-detail="modulate" aria-pressed="false">
      <span class="vdi-step-number">04</span>
      <span class="vdi-step-title">Modulate</span>
      <span class="vdi-step-caption">Turn bits into sound</span>
    </button>

    <span class="vdi-arrow vdi-arrow-channel" aria-hidden="true">→</span>

    <button type="button" class="vdi-step vdi-step-channel" data-detail="channel" aria-pressed="false">
      <span class="vdi-step-number">05</span>
      <span class="vdi-step-title">Audio channel</span>

      <svg class="vdi-wave" viewBox="0 0 180 45" role="img" aria-label="Audio waveform">
        <path d="M2 23
             C12 23, 12 8, 22 8
             S32 38, 42 38
             S52 13, 62 13
             S72 31, 82 31
             S92 5, 102 5
             S112 40, 122 40
             S132 16, 142 16
             S152 27, 162 27
             S170 23, 178 23" />
      </svg>

      <span class="vdi-step-caption">The signal meets the codec</span>
    </button>

    <span class="vdi-arrow vdi-arrow-channel" aria-hidden="true">→</span>

    <button type="button" class="vdi-step" data-detail="recover" aria-pressed="false">
      <span class="vdi-step-number">06</span>
      <span class="vdi-step-title">Recover</span>
      <span class="vdi-step-caption">Decode, repair and decompress</span>
    </button>

    <span class="vdi-arrow" aria-hidden="true">→</span>

    <button type="button" class="vdi-step vdi-step-result" data-detail="result" aria-pressed="false">
      <span class="vdi-step-number">07</span>
      <span class="vdi-step-title">Recovered data</span>
      <span class="vdi-step-caption">Measured up to 2 KB/s</span>
    </button>
  </div>

  <div class="vdi-bridge-details" aria-live="polite">
    <div class="vdi-detail-heading">
      <span class="vdi-detail-index" id="vdiDetailIndex">01</span>
      <h3 id="vdiDetailTitle">Raw data</h3>
    </div>

    <p id="vdiDetailText">
      VDI Bridge accepts arbitrary binary information, including files,
      clipboard content and short control messages. The audio path carries
      raw bytes rather than Base64-encoded text.
    </p>

    <dl class="vdi-detail-facts" id="vdiDetailFacts">
      <div>
        <dt>Input</dt>
        <dd>Arbitrary binary data</dd>
      </div>
      <div>
        <dt>Use cases</dt>
        <dd>Files, clipboard and control</dd>
      </div>
    </dl>
  </div>

  <figcaption>
    VDI Bridge transmission pipeline. Select a stage to inspect the
    corresponding engineering decision.
  </figcaption>
</figure>

<style>
  .vdi-bridge-diagram,
  .vdi-bridge-diagram * {
    box-sizing: border-box;
  }

  .vdi-bridge-diagram {
    --vdi-text: #18212f;
    --vdi-muted: #687078;
    --vdi-line: #c9ced3;
    --vdi-border: #d9dde1;
    --vdi-border-strong: #c9ced6;
    --vdi-surface: #ffffff;
    --vdi-surface-soft: #fbfcfd;
    --vdi-soft: #f5f6f7;
    --vdi-step-bg: rgba(255, 255, 255, 0.78);
    --vdi-step-active: linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(246,248,250,0.98) 100%);
    --vdi-detail-bg: linear-gradient(180deg, rgba(251,252,253,0.96) 0%, rgba(246,248,250,0.98) 100%);
    --vdi-shadow: 0 14px 38px rgba(24, 33, 47, 0.08);
    --vdi-data: #2563a6;
    --vdi-channel: #bf642f;
    --vdi-result: #287451;

    width: 100%;
    margin: 2.5rem 0;
    color: var(--vdi-text);
    text-align: left;
    font: inherit;
  }

  .vdi-bridge-diagram[data-vdi-theme="light"] {
    padding: 0.85rem 0.9rem 0.75rem;
    border: 1px solid rgba(117,137,168,0.2);
    border-radius: 24px;
    background:
      radial-gradient(circle at top right, rgba(30,95,216,0.06) 0%, transparent 34%),
      radial-gradient(circle at bottom left, rgba(0,158,119,0.05) 0%, transparent 36%),
      linear-gradient(180deg, rgba(250,252,255,0.98) 0%, rgba(244,248,253,0.98) 100%);
    box-shadow: 0 18px 40px rgba(18,35,60,0.08);
  }

  .post-content figure.vdi-bridge-diagram[data-vdi-theme="light"] {
    padding: 0.85rem 0.9rem 0.75rem;
    border: 1px solid rgba(117,137,168,0.2);
    border-radius: 24px;
    background:
      radial-gradient(circle at top right, rgba(30,95,216,0.06) 0%, transparent 34%),
      radial-gradient(circle at bottom left, rgba(0,158,119,0.05) 0%, transparent 36%),
      linear-gradient(180deg, rgba(250,252,255,0.98) 0%, rgba(244,248,253,0.98) 100%);
    box-shadow: 0 18px 40px rgba(18,35,60,0.08);
  }

  .vdi-bridge-diagram[data-vdi-theme="dark"] {
    --vdi-text: #e4edf8;
    --vdi-muted: #a9b0b7;
    --vdi-line: #586069;
    --vdi-border: #41474e;
    --vdi-border-strong: #566171;
    --vdi-surface: transparent;
    --vdi-surface-soft: #0f1621;
    --vdi-soft: rgba(255, 255, 255, 0.05);
    --vdi-step-bg: rgba(255, 255, 255, 0.018);
    --vdi-step-active: linear-gradient(180deg, rgba(23, 31, 43, 0.98) 0%, rgba(18, 25, 36, 0.98) 100%);
    --vdi-detail-bg: linear-gradient(180deg, rgba(18, 25, 36, 0.98) 0%, rgba(12, 18, 28, 0.98) 100%);
    --vdi-shadow: 0 18px 42px rgba(0, 0, 0, 0.24);
    --vdi-data: #72aee6;
    --vdi-channel: #e5915f;
    --vdi-result: #63b98b;
  }

  .vdi-bridge-visual {
    display: grid;
    grid-template-columns:
      minmax(0, 1.15fr) 22px
      minmax(0, 1fr) 22px
      minmax(0, 1fr) 22px
      minmax(0, 1fr) 22px
      minmax(0, 1.15fr) 22px
      minmax(0, 1fr) 22px
      minmax(0, 1.15fr);
    align-items: stretch;
    width: 100%;
    gap: 0.2rem;
    overflow-x: auto;
    padding: 0.2rem 0 0.35rem;
    scrollbar-width: thin;
  }

  .vdi-step {
    appearance: none;
    min-width: 0;
    padding: 0.92rem 0.9rem 0.88rem;
    color: inherit;
    text-align: left;
    background: var(--vdi-step-bg);
    border: 1px solid var(--vdi-border);
    border-radius: 14px;
    cursor: pointer;
    font: inherit;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.03);
    transition:
      background-color 140ms ease,
      border-color 140ms ease,
      transform 140ms ease,
      box-shadow 140ms ease;
  }

  .vdi-step:hover,
  .vdi-step:focus-visible,
  .vdi-step[aria-pressed="true"] {
    background: var(--vdi-step-active);
    border-color: var(--vdi-border-strong);
    box-shadow: var(--vdi-shadow);
    transform: translateY(-1px);
    outline: none;
  }

  .vdi-step-number {
    display: block;
    margin-bottom: 0.65rem;
    color: var(--vdi-muted);
    font-size: 0.7rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.08em;
  }

  .vdi-step-title {
    display: block;
    margin-bottom: 0.45rem;
    font-weight: 700;
    line-height: 1.25;
  }

  .vdi-step-caption {
    display: block;
    color: var(--vdi-muted);
    font-size: 0.75rem;
    line-height: 1.4;
  }

  .vdi-step-channel {
    border-top: 3px solid var(--vdi-channel);
  }

  .vdi-step-channel .vdi-step-number,
  .vdi-step-channel .vdi-step-title {
    color: var(--vdi-channel);
  }

  .vdi-step-result {
    border-top: 3px solid var(--vdi-result);
  }

  .vdi-step-result .vdi-step-number,
  .vdi-step-result .vdi-step-title {
    color: var(--vdi-result);
  }

  .vdi-arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--vdi-line);
    font-size: 1rem;
    opacity: 0.9;
  }

  .vdi-arrow-channel {
    color: var(--vdi-channel);
  }

  .vdi-wave {
    display: block;
    width: 100%;
    height: 30px;
    margin: 0.12rem 0 0.4rem;
  }

  .vdi-wave path {
    fill: none;
    stroke: var(--vdi-channel);
    stroke-width: 2.5;
    stroke-linecap: round;
  }

  .vdi-bridge-details {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 360px;
    column-gap: 1rem;
    row-gap: 0.7rem;
    margin-top: 0.45rem;
    padding: 1.05rem 1.1rem 1rem;
    background: var(--vdi-detail-bg);
    border: 1px solid var(--vdi-border);
    border-radius: 18px;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.03);
  }

  .vdi-bridge-diagram[data-vdi-theme="light"] .vdi-bridge-details {
    border-color: rgba(117,137,168,0.22);
    box-shadow: 0 10px 26px rgba(18,35,60,0.06), inset 0 1px 0 rgba(255,255,255,0.7);
  }

  .vdi-detail-heading {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    grid-column: 1;
    grid-row: 1;
  }

  .vdi-detail-heading h3 {
    margin: 0;
    font: inherit;
    font-size: 1.24rem;
    font-weight: 700;
  }

  .vdi-detail-index {
    color: var(--vdi-data);
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
  }

  .vdi-bridge-details > p {
    margin: 0;
    color: var(--vdi-muted);
    line-height: 1.68;
    max-width: 62ch;
    text-align: left;
    grid-column: 1;
    grid-row: 2;
  }

  .vdi-detail-facts {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.7rem;
    margin: 0;
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: start;
  }

  .vdi-detail-facts div {
    position: relative;
    width: 100%;
    min-width: 0;
    padding: 0.82rem 0.95rem 0.84rem;
    border: 1px solid var(--vdi-border);
    border-radius: 16px;
    background:
      linear-gradient(180deg, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0.015) 100%),
      rgba(255,255,255,0.02);
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.025);
    overflow: hidden;
  }

  .vdi-bridge-diagram[data-vdi-theme="light"] .vdi-detail-facts div {
    background:
      linear-gradient(180deg, rgba(255,255,255,0.92) 0%, rgba(248,251,255,0.98) 100%),
      rgba(255,255,255,0.75);
    box-shadow: 0 8px 18px rgba(18,35,60,0.05), inset 0 1px 0 rgba(255,255,255,0.85);
  }

  .vdi-detail-facts div::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 3px;
    background: linear-gradient(180deg, var(--vdi-data) 0%, rgba(114,174,230,0.24) 100%);
  }

  .vdi-detail-facts dt {
    display: inline-flex;
    align-items: center;
    margin-bottom: 0.45rem;
    padding: 0.18rem 0.45rem;
    border-radius: 999px;
    background: rgba(255,255,255,0.04);
    color: var(--vdi-muted);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }

  .vdi-detail-facts dd {
    margin: 0;
    font-size: 0.94rem;
    font-weight: 700;
    line-height: 1.38;
    color: var(--vdi-text);
  }

  .vdi-bridge-diagram figcaption {
    margin-top: 0.85rem;
    color: var(--vdi-muted);
    font-size: 0.74rem;
    line-height: 1.5;
    text-align: center;
  }

  .vdi-bridge-diagram[data-vdi-theme="light"] figcaption {
    color: #6d7f98;
  }

  @media (max-width: 1180px) {
    .vdi-bridge-visual {
      width: max-content;
      min-width: 100%;
    }

    .vdi-step {
      min-width: 152px;
    }
  }

  @media (max-width: 720px) {
    .vdi-bridge-details {
      grid-template-columns: 1fr;
      padding: 1rem;
    }

    .vdi-detail-heading,
    .vdi-bridge-details > p,
    .vdi-detail-facts {
      grid-column: auto;
      grid-row: auto;
    }

    .vdi-detail-facts {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  @media (max-width: 460px) {
    .vdi-detail-facts {
      display: grid;
      grid-template-columns: 1fr;
    }
  }
</style>

<script>
  (() => {
    "use strict";

    const diagram = document.getElementById("vdiBridgeDiagram");
    if (!diagram) return;

    function resolveVdiTheme() {
      let storedTheme = null;
      try {
        storedTheme = localStorage.getItem("theme");
      } catch (error) {
        storedTheme = null;
      }

      const htmlTheme = document.documentElement.getAttribute("data-theme");

      if (htmlTheme === "light" || storedTheme === "light") {
        return "light";
      }

      if (htmlTheme === "dark" || storedTheme === "dark") {
        return "dark";
      }

      return "dark";
    }

    function applyVdiTheme() {
      diagram.setAttribute("data-vdi-theme", resolveVdiTheme());
    }

    applyVdiTheme();

    new MutationObserver(applyVdiTheme).observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["data-theme"]
    });

    const stages = diagram.querySelectorAll(".vdi-step");
    const detailIndex = diagram.querySelector("#vdiDetailIndex");
    const detailTitle = diagram.querySelector("#vdiDetailTitle");
    const detailText = diagram.querySelector("#vdiDetailText");
    const detailFacts = diagram.querySelector("#vdiDetailFacts");

    const details = {
      source: {
        index: "01",
        title: "Raw data",
        text:
          "VDI Bridge accepts arbitrary binary information, including files, clipboard content and short control messages. The audio path carries raw bytes rather than Base64-encoded text.",
        facts: [
          ["Input", "Arbitrary binary data"],
          ["Use cases", "Files, clipboard and control"]
        ]
      },

      prepare: {
        index: "02",
        title: "Prepare",
        text:
          "The payload is compressed with GZip before transmission and wrapped in a frame that describes its length, type and integrity information. Compression is performed before error-correction redundancy is added.",
        facts: [
          ["Compression", "GZip"],
          ["Purpose", "Reduce the number of transmitted symbols"]
        ]
      },

      protect: {
        index: "03",
        title: "Protect",
        text:
          "Reed-Solomon coding allows the receiver to reconstruct damaged bytes. Sixteen-way interleaving distributes consecutive bytes across separate blocks, converting burst damage into smaller, correctable errors.",
        facts: [
          ["Error correction", "RS(255,191)"],
          ["Interleaving", "16 blocks deep"]
        ]
      },

      modulate: {
        index: "04",
        title: "Modulate",
        text:
          "The protected bitstream is mapped to audio carriers. OOK became the reliable default because carrier presence survived the lossy channel more consistently than precise phase or amplitude values.",
        facts: [
          ["Default modulation", "OOK"],
          ["Signal structure", "80 carriers · 10 ms symbols"]
        ]
      },

      channel: {
        index: "05",
        title: "Audio channel",
        text:
          "The waveform passes through the VDI audio engine, the Citrix codec, the network path and the endpoint audio stack. Measurements showed that the useful carrier range was approximately 600 Hz to 8.5 kHz.",
        facts: [
          ["Audio format", "48 kHz"],
          ["Reliable range", "600–8,500 Hz"]
        ]
      },

      recover: {
        index: "06",
        title: "Recover",
        text:
          "WASAPI loopback captures the received stereo audio. The receiver detects the transmission, extracts the symbols, reverses the interleaving, corrects damaged bytes and decompresses the original payload.",
        facts: [
          ["Capture", "WASAPI loopback"],
          ["Recovery", "Decode → correct → verify → decompress"]
        ]
      },

      result: {
        index: "07",
        title: "Recovered data",
        text:
          "The final implementation supports clipboard content, file transfer and control traffic. Bulk data uses stereo, while short control messages use a more conservative mono configuration.",
        facts: [
          ["Measured throughput", "Up to 2 KB/s"],
          ["Bulk channel", "Stereo"]
        ]
      }
    };

    function renderFacts(items) {
      detailFacts.replaceChildren();

      items.forEach(([term, description]) => {
        const wrapper = document.createElement("div");
        const dt = document.createElement("dt");
        const dd = document.createElement("dd");

        dt.textContent = term;
        dd.textContent = description;

        wrapper.append(dt, dd);
        detailFacts.appendChild(wrapper);
      });
    }

    function activate(stage) {
      const key = stage.dataset.detail;
      const selected = details[key];
      if (!selected) return;

      stages.forEach((item) => {
        item.setAttribute(
          "aria-pressed",
          item === stage ? "true" : "false"
        );
      });

      detailIndex.textContent = selected.index;
      detailTitle.textContent = selected.title;
      detailText.textContent = selected.text;
      renderFacts(selected.facts);
    }

    stages.forEach((stage) => {
      stage.addEventListener("mouseenter", () => activate(stage));
      stage.addEventListener("focus", () => activate(stage));
      stage.addEventListener("click", () => activate(stage));
    });
  })();
</script>

<h2 id="what-is-worth-remembering-from-this-article" class="post-h2-center post-h2-emphasis">What Is Worth Remembering From This Article</h2>

<ul>
  <li>Offensive Security starts with questions, not tools.</li>
  <li>Every system is a collection of communication channels.</li>
  <li>Understanding the physics matters more than choosing the algorithm.</li>
  <li>The codec was the real bottleneck, not the modulation.</li>
  <li>Good measurements are worth more than good assumptions.</li>
  <li>A simple system that works is better than a sophisticated system that does not survive the channel.</li>
  <li>Almost every engineering decision was made after an experiment failed.</li>
  <li>Writing the code was the easiest part of the project; understanding was the hardest.</li>
</ul>

<p>When I look at VDI Bridge today, I no longer see a system that transfers information through sound. I see a learning process. The research started with a question about audio, continued through the world of digital communications, and ended with a new way of looking at systems. The speaker was only the starting point. The most important insight was that as long as there is a way to transfer information between two points, there is also a way to treat it as a communication channel. Once you start thinking that way, it becomes very hard to look at systems in any other way.</p>

<div class="research-credit-note">
  <p class="research-credit-label">Looking For The Short Version?</p>
  <p>This article is the full engineering deep dive. For the shorter overview focused on the mindset shift and the core lessons, read <a href="/2026/07/21/audio-is-not-just-output-its-a-channel/">Audio Is Not Just Output. It Is a Channel.</a></p>
</div>]]></content><author><name>Daniel Oz</name></author><category term="offensive-security" /><category term="citrix" /><category term="vdi" /><category term="covert-channel" /><category term="communication-engineering" /><category term="red-team" /><category term="research" /><summary type="html"><![CDATA[A full engineering deep dive into the Citrix audio research, covering wave fundamentals, channel constraints, measurements, and the design logic behind VDI Bridge.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danieloz147.github.io/assets/images/citrix-audio/image1.png" /><media:content medium="image" url="https://danieloz147.github.io/assets/images/citrix-audio/image1.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Rogue Sandbox - When the Scan Itself is the Payload</title><link href="https://danieloz147.github.io/2026/07/05/rogue-sandbox-when-the-scan-is-the-payload/" rel="alternate" type="text/html" title="Rogue Sandbox - When the Scan Itself is the Payload" /><published>2026-07-05T00:00:00+00:00</published><updated>2026-07-05T00:00:00+00:00</updated><id>https://danieloz147.github.io/2026/07/05/rogue-sandbox-when-the-scan-is-the-payload</id><content type="html" xml:base="https://danieloz147.github.io/2026/07/05/rogue-sandbox-when-the-scan-is-the-payload/"><![CDATA[<h2 id="tldr">TL;DR</h2>

<ul>
  <li>Rogue Sandbox is not a sandbox escape; it is a trust-model abuse pattern inside legitimate browser boundaries.</li>
  <li>The critical shift happens when users grant folder-level <code class="language-plaintext highlighter-rouge">readwrite</code> permission through File System Access API.</li>
  <li>In this model, narrative design can make write-capable actions feel like harmless scanning or cleanup.</li>
  <li>There may be no classic download moment, yet meaningful local file modification can still occur.</li>
  <li>Chromium can route some write events into Safe Browsing or Download Protection logic, depending on file and policy context.</li>
  <li>The defensive challenge is not only exploit prevention, but governance of browser-granted local file permissions.</li>
</ul>

<nav class="post-toc" aria-label="Table of contents">
	<h3>Table of Contents</h3>
	<ol>
		<li><a href="#abstract">Abstract</a></li>
		<li><a href="#research-scope">Research Scope</a></li>
		<li>
			<a href="#background-and-motivation">Background and Motivation</a>
			<ol>
				<li><a href="#the-idea">The Idea</a></li>
				<li><a href="#the-question">The question</a></li>
				<li><a href="#why-rogue-sandbox">Why Rogue Sandbox?</a></li>
				<li><a href="#the-concealment-is-not-technical-it-is-narrative">The Concealment Is Not Technical. It Is Narrative.</a></li>
			</ol>
		</li>
		<li>
			<a href="#deep-dive-file-system-access-api">Deep Dive: File System Access API</a>
			<ol>
				<li><a href="#what-the-api-enables-conceptually">What the API Enables Conceptually</a></li>
				<li><a href="#pseudo-code-and-source-level-perspective">Pseudo-Code and Source-Level Perspective</a></li>
				<li><a href="#secure-context-and-user-gesture">Secure Context and User Gesture</a></li>
				<li><a href="#directory-handle-read-and-readwrite">Directory Handle, read, and readwrite</a></li>
				<li><a href="#what-happens-under-the-hood-in-chromium">What Happens Under the Hood in Chromium</a></li>
			</ol>
		</li>
		<li>
			<a href="#from-theory-to-practice">From Theory to Practice</a>
			<ol>
				<li><a href="#client-side-file-infection">Client-Side File Infection</a></li>
				<li><a href="#technical-limitations">Technical Limitations</a></li>
				<li><a href="#building-the-story-the-narrative-is-part-of-the-technique">Building the Story</a></li>
				<li><a href="#injection-architecture">Injection Architecture</a></li>
				<li><a href="#client-side-infection-pipeline">Client-Side Infection Pipeline</a></li>
				<li><a href="#what-the-code-must-account-for">What the Code Must Account For</a></li>
				<li><a href="#mark-of-the-web-when-you-edit-a-file-instead-of-downloading-it">Mark of the Web</a></li>
				<li><a href="#smartscreen-and-what-happens-when-there-is-no-download">SmartScreen and No-Download Flows</a></li>
				<li><a href="#chrome-vs-edge">Chrome vs. Edge</a></li>
			</ol>
		</li>
		<li><a href="#demo">Demo</a></li>
		<li><a href="#what-can-be-done-defensively">Defensive Recommendations</a></li>
		<li><a href="#conclusion">Conclusion</a></li>
		<li><a href="#research-notes">Research Notes</a></li>
		<li><a href="#sources">Sources</a></li>
	</ol>
</nav>

<h2 id="abstract" class="post-h2-center post-h2-emphasis">Abstract</h2>

<p>For years, we have treated the browser as an isolated execution environment. As long as there was no exploit, no downloaded file, and no local executable, the assumption was that a website’s impact on the user’s filesystem remained limited.</p>

<p>Modern Web APIs are beginning to blur that boundary. The File System Access API allows, after user approval, read and write operations over local files and folders. For Web developers, this is a legitimate capability that enables advanced in-browser applications. For an attacker, this is not a new vulnerability. It is a new trust pivot.</p>

<p>The trigger for this research was the Check Point Research article that demonstrated an attack path built entirely on the File System Access API, with no exploit and no local payload. Reading it raised a broader question: what happens when we stop framing this only as browser-based ransomware, and instead analyze the same primitive under a wider threat model?</p>

<p>In this article, I present Rogue Sandbox, the research process that led to it, including failed attempts, Chromium constraints, and the relationship to Mark of the Web (MOTW). I then focus on a core insight from the work: in this scenario, there is not necessarily a “download moment”. From the user’s perspective, this is a scan action. From the browser’s perspective, this is write authorization. The gap between those two models is the starting point of this research.</p>

<hr />

<figure class="figure-centered">
	<img src="/assets/images/rogue-sandbox/image1.png" alt="Rogue Sandbox overview: what the user believes versus what the browser actually authorizes" />
	<figcaption>Figure 1 - The entire article in one image</figcaption>
</figure>

<h2 id="research-scope">Research Scope</h2>

<p>All demos, code samples, and projects presented in this article were developed strictly for research purposes and tested in a controlled lab environment on dedicated test files.</p>

<p>The goal of this work is to document the research process, findings, and insights that emerged during the investigation. If any third party chooses to use the code, ideas, or techniques described here for unauthorized or harmful activity, that use is their sole responsibility and does not reflect the purpose of this research or the author’s intent.</p>

<h3 id="credit-to-check-point-research">Credit to Check Point Research</h3>

<p>Before diving deeper, it is important to provide explicit credit to the research that served as the starting point for this work.</p>

<p>In July 2026, Check Point Research published <a href="https://research.checkpoint.com/2026/browser-only-ransomware-from-llm-hallucinations-to-a-practical-attack-technique/" target="_blank" rel="noopener noreferrer"><strong>Browser-Only Ransomware: From LLM Hallucinations to a Practical Attack Technique</strong></a>, showing how an idea originating from LLM output can evolve into a practical attack technique built on the File System Access API. Their work demonstrated that combining social engineering, user-granted permissions, and native browser capabilities can result in local file modification without an exploit, without a local payload, and without bypassing operating system security mechanisms.</p>

<p>For me, that was the “what if?” moment. If a website can, after user approval, obtain write permission to a local folder and operate on files inside it, is browser-based ransomware really the main story? Or is it only one example of a broader trust model?</p>

<p>The Check Point article was the trigger for the research presented here. Everything that follows, from the initial PoC through Chromium source analysis to the Rogue Sandbox model, was driven by the attempt to answer that question.</p>

<h2 id="background-and-motivation">Background and Motivation</h2>

<p>To understand the risk, we need to start with the gap between how users perceive a scan action and how the browser’s permission model actually works.</p>

<p>Over time, average users have learned to be at least somewhat cautious around externally delivered files. An email attachment, an executable downloaded from the internet, or an Office document with macros is usually perceived as an unusual event that may indicate risk.</p>

<p>But when that same user visits a website that looks like a security tool, clicks Scan, and selects a folder through a legitimate system dialog, the suspicion model shifts. From the user’s perspective, this is not running foreign code or installing software, but activating a tool intended to protect their files.</p>

<p>That is exactly the key point.</p>

<p>A legitimate scan flow and local file modification can begin with the same permission grant. If the site receives <code class="language-plaintext highlighter-rouge">read</code> access, it can map and analyze folder content. If it receives <code class="language-plaintext highlighter-rouge">readwrite</code> access, it can also modify files within that folder. From Chromium’s perspective, these are two levels of the same permission family. From the user’s perspective, both may look like a natural part of the scanning process.</p>

<p>This transition echoes previous research focused on abusing trust mechanisms instead of exploiting software vulnerabilities. In my previous iOS article, I analyzed configuration profiles, a legitimate mechanism that allows users to expand the device’s trust circle. The core principle is similar here: not to bypass the sandbox, but to get the user to grant it permission.</p>

<figure class="figure-centered">
	<img src="/assets/images/rogue-sandbox/image2.png" alt="From user action to system-level permission outcome" />
	<figcaption>Figure 2 - From the User to the System</figcaption>
</figure>

<h3 id="the-idea">The Idea</h3>

<p>The Check Point demo used a lure that looked like an AI image enhancement tool. That was a highly logical choice. A user who wants to improve an image understands why the site asks for file access and even expects the file to change as part of processing. <strong>During my own research, I chose a different direction.</strong></p>

<p>Instead of an AI tool, I built a flow that looked like a malware scanner. Not because this is the only possible lure, but because it allowed a more interesting research question: how does user behavior change when the user believes they are operating a tool that protects their files?</p>

<p>In that scenario, the user does not expect files to be transformed into a new output, as with image tools. The expectation is that files will be scanned, analyzed, and, when needed, “cleaned”. The defensive narrative makes the permission request feel even more natural. <strong>The user does not experience this as granting a website write access to a local folder. The user experiences this as allowing a security tool to do its job.</strong></p>

<p>From the browser’s perspective, however, there is no difference between a site presenting itself as an AI tool, an image editor, or a malware scanner. Chromium does not evaluate the site’s story or declared intent. It checks whether the origin has the required permissions, and if so, allows operations that the API permits under that permission scope.</p>

<p>At that point, the question was no longer whether browser-based ransomware could be built. The question became whether it was possible to build a user experience in which a site perceived as a security product receives <code class="language-plaintext highlighter-rouge">readwrite</code> access while the user believes they only started a routine scan.</p>

<p>In other words, I was not primarily interested in whether browser-side encryption is possible. I wanted to understand how a fully defensive-looking narrative can lead users to grant write permission over their own files without experiencing the event as dangerous.</p>

<p>That led to the central research question in this article: can the File System Access API trust model be abused to modify existing files and inject payload content without the user experiencing the event as an infection?</p>

<h3 id="the-question">The Question</h3>

<p>The first question was simple: is this relevant to Chrome Desktop?</p>

<p>The answer was yes. The File System Access API is supported across multiple Chromium-based environments and allows opening a picker, obtaining a folder handle, and operating on files inside that folder according to granted permissions.</p>

<p>Once that was answered, the more important question shifted.</p>

<p>I was no longer asking only whether browser-based ransomware is possible. I was asking whether a believable scanner experience could be built while the browser receives <code class="language-plaintext highlighter-rouge">readwrite</code> permission and can modify local files inside the granted folder.</p>

<p>The research objective was therefore not to prove that browser encryption can happen. Previous work already demonstrated that. The objective here was to map the trust chain:</p>

<ul>
  <li>What story causes users to approve?</li>
  <li>What does the browser actually show?</li>
  <li>Where are the hard limits?</li>
  <li>What happens on mobile?</li>
  <li>What happens in non-Chromium browsers?</li>
  <li>What happens with sensitive or system folders?</li>
  <li>Does MOTW mark affected files?</li>
  <li>Do defenses focused on downloads even classify this event the same way?</li>
</ul>

<h3 id="why-rogue-sandbox">Why Rogue Sandbox?</h3>

<p>Precision matters here - <span class="rogue-accent-line" style="color: #ff6b6b; font-weight: 700;">Rogue Sandbox is not another name for sandbox escape.</span></p>

<p>This is not a site breaking isolation boundaries, bypassing Chromium, or obtaining unrestricted filesystem access. The opposite is true. The model is interesting precisely because the browser remains within its intended boundaries: secure context, user gesture, picker flow, user-granted permission, browser support constraints, protected folder restrictions, and permission scope (<code class="language-plaintext highlighter-rouge">read</code> or <code class="language-plaintext highlighter-rouge">readwrite</code>).</p>

<p>Rogue Sandbox points to something else: a workflow the user perceives as safe.</p>

<p>In security terminology, sandbox typically describes an isolated environment where suspicious files are analyzed without risking the host. In this model, however, the user is not placing a suspicious file into an isolated environment. The user is placing an entire local folder into a web workflow that appears to be a scanner, while that workflow itself may become untrusted.</p>

<p>The user sees Scan. In practice, Write may occur.
The user sees Clean. In practice, Modify may occur.
The user sees Quarantine. In practice, the local file may be altered.</p>

<p>Rogue Sandbox is not a case where the browser escapes its sandbox. Rogue Sandbox is a case where the user puts files into a testing environment that appears safe, while that environment receives the capability to change them.</p>

<h3 id="the-concealment-is-not-technical-it-is-narrative">The Concealment Is Not Technical. It Is Narrative.</h3>

<p>In this scenario, the user is not necessarily blind to what is happening. They see a website, a button, a folder picker, a progress bar, and status labels such as Analyzing, Uploading, Cleaning, Quarantining, or Repairing. The activity is often happening in front of them, not behind their back.</p>

<p>The problem is interpretation through narrative.</p>

<p>If the story is malware scanning, users expect file interaction. If the story is AI-powered malware cleanup, users may even accept file edits or repairs as normal behavior. That is exactly where permission becomes dangerous: the same prompt can be interpreted by the browser as “grant website write access” and by the user as “allow scanner to clean files”.</p>

<p>This is a critical point in the research. For the browser, the permission prompt is a formal consent event. For the user, it is often one step toward a promised outcome. If the promised outcome is protection, cleanup, or repair, write access may not be perceived as risky at all.</p>

<figure class="figure-centered">
	<img src="/assets/images/rogue-sandbox/image3.png" alt="The story the user sees versus the technical reality" />
	<figcaption>Figure 3 - The Story the User Sees vs. the Technical Reality</figcaption>
</figure>

<h2 id="deep-dive-file-system-access-api">Deep Dive: File System Access API</h2>

<h3 id="what-the-api-enables-conceptually">What the API Enables Conceptually</h3>

<p>The File System Access API was designed to let web applications work with local files in a richer way.</p>

<p>In the older model, a site typically received a file through <code class="language-plaintext highlighter-rouge">input type="file"</code> and could read it as a <code class="language-plaintext highlighter-rouge">Blob</code>. With the newer model, browser behavior is much closer to local applications: the user selects a file or folder, the browser returns a handle, and the app can perform actions based on granted permissions.</p>

<p>The most important point is that this API is not only about upload. After user approval, it enables reading files and saving changes directly back to local files and folders. When a folder is selected, an app can enumerate its content, iterate entries, request file and directory handles, and build a complete local processing flow.</p>

<p>For legitimate product use, this is powerful and useful. A browser-based code editor needs to open a project folder. An image editor needs to save edits to an existing file. A conversion tool needs to write outputs.</p>

<p>From an attacker’s perspective, the same mechanism creates a different story: <strong>the user gives a web process access to local files, and the boundary between scanning and modifying becomes a matter of developer intent.</strong></p>

<p>At a high level:</p>

<div class="attack-chain" role="img" aria-label="High-level File System Access flow from user action to local file modification">
	<span class="step">User Click</span>
	<span class="arrow">→</span>
	<span class="step">showDirectoryPicker()</span>
	<span class="arrow">→</span>
	<span class="step">Directory Handle</span>
	<span class="arrow">→</span>
	<span class="step">Permission Granted</span>
	<span class="arrow">→</span>
	<span class="step">Writable Handle</span>
	<span class="arrow">→</span>
	<span class="step">Local File Modification</span>
</div>

<h3 id="pseudo-code-and-source-level-perspective">Pseudo-Code and Source-Level Perspective</h3>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Safe defensive pseudo-code, not an implementation of file modification.</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="dl">'</span><span class="s1">showDirectoryPicker</span><span class="dl">'</span> <span class="k">in</span> <span class="nb">window</span><span class="p">))</span> <span class="p">{</span>
	<span class="nx">showUnsupportedBrowserMessage</span><span class="p">();</span>
<span class="p">}</span>

<span class="nx">button</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">click</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
	<span class="c1">// Must be triggered by a user gesture.</span>
	<span class="kd">const</span> <span class="nx">dir</span> <span class="o">=</span> <span class="k">await</span> <span class="nb">window</span><span class="p">.</span><span class="nx">showDirectoryPicker</span><span class="p">({</span>
		<span class="na">mode</span><span class="p">:</span> <span class="dl">'</span><span class="s1">readwrite</span><span class="dl">'</span><span class="p">,</span>
		<span class="na">startIn</span><span class="p">:</span> <span class="dl">'</span><span class="s1">documents</span><span class="dl">'</span>
	<span class="p">});</span>

	<span class="kd">const</span> <span class="nx">permission</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">dir</span><span class="p">.</span><span class="nx">queryPermission</span><span class="p">({</span> <span class="na">mode</span><span class="p">:</span> <span class="dl">'</span><span class="s1">readwrite</span><span class="dl">'</span> <span class="p">});</span>
	<span class="k">if</span> <span class="p">(</span><span class="nx">permission</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">granted</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">await</span> <span class="nx">dir</span><span class="p">.</span><span class="nx">requestPermission</span><span class="p">({</span> <span class="na">mode</span><span class="p">:</span> <span class="dl">'</span><span class="s1">readwrite</span><span class="dl">'</span> <span class="p">});</span>
	<span class="p">}</span>

	<span class="c1">// Defensive lesson: this is the trust boundary.</span>
	<span class="nx">logSecurityEvent</span><span class="p">(</span><span class="dl">'</span><span class="s1">origin received directory read/write access</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<figure class="figure-centered">
	<img class="img-crisp" src="/assets/images/rogue-sandbox/Image4.png" alt="Chromium validates permission, not intent" />
	<figcaption>Figure 4 - Chromium validates permission, not intent.</figcaption>
</figure>

<p>As shown above, the browser’s emphasis is the permission decision point, not the downstream implementation of file changes. In practice, browser logic is centered on status and request validation, not on whether the site’s intent is benevolent.</p>

<p class="key-clarifications-title">Key clarifications:</p>

<h3 id="secure-context-and-user-gesture">Secure Context and User Gesture</h3>

<p>The API is not expected to run silently in the background. <code class="language-plaintext highlighter-rouge">showDirectoryPicker()</code> requires a secure context (typically HTTPS) and transient user activation, meaning an explicit user action such as clicking a button. If the call is not made from such interaction, the browser should block it.</p>

<p>This is an important safeguard, but it is not sufficient when the model depends on persuading users to perform a legitimate action.</p>

<p>In other words, the user-gesture requirement does not prevent phishing. It prevents automatic picker invocation without user action. If a site can convince a user to click a well-framed button inside a believable narrative, the requirement is satisfied.</p>

<h3 id="directory-handle-read-and-readwrite">Directory Handle, <code class="language-plaintext highlighter-rouge">read</code>, and <code class="language-plaintext highlighter-rouge">readwrite</code></h3>

<p>When the user selects a folder, the site does not receive a conventional absolute path. It receives a <code class="language-plaintext highlighter-rouge">FileSystemDirectoryHandle</code>. The handle represents the selected object and allows operations through the API.</p>

<p>The critical distinction is permission mode: <code class="language-plaintext highlighter-rouge">read</code> versus <code class="language-plaintext highlighter-rouge">readwrite</code>.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">read</code> allows reading and enumeration.</li>
  <li><code class="language-plaintext highlighter-rouge">readwrite</code> also enables persisting changes and, depending on implementation and permission scope, creating, modifying, or deleting files.</li>
</ul>

<p>In the Rogue Sandbox scenario, <code class="language-plaintext highlighter-rouge">readwrite</code> is the core pivot. Without write access, the story remains scanning, metadata collection, or analysis. With write access, the same story can become fake cleanup, fake quarantine, fake repair, or direct modification of local files.</p>

<h3 id="primitive-to-risk-mapping">Primitive-to-Risk Mapping</h3>

<div class="risk-map-wrap">
	<table class="risk-map-table">
		<thead>
			<tr>
				<th>Primitive</th>
				<th>Legitimate Use</th>
				<th>Risk Point</th>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td><code>showDirectoryPicker</code></td>
				<td>Select a folder for local work</td>
				<td>User may select an overly broad scope</td>
			</tr>
			<tr>
				<td><code>FileSystemDirectoryHandle</code></td>
				<td>Represent the selected directory</td>
				<td>The handle turns that directory into the site's working set</td>
			</tr>
			<tr>
				<td><code>entries()</code> / <code>values()</code></td>
				<td>Iterate files and subdirectories</td>
				<td>Enables local content mapping within granted scope</td>
			</tr>
			<tr>
				<td><code>getFileHandle</code></td>
				<td>Access a file inside the selected directory</td>
				<td>Operations can affect files the user did not evaluate individually</td>
			</tr>
			<tr>
				<td><code>readwrite</code> permission</td>
				<td>Save edits or repair files</td>
				<td>The critical transition from scanning to modification</td>
			</tr>
		</tbody>
	</table>
</div>

<h3 id="platform-scope-not-universally-applicable">Platform Scope: Not Universally Applicable</h3>

<p>At this stage, one point is important to emphasize: this capability is not universal.</p>

<p>Chrome and Chromium are the center of this story. Firefox and Safari do not provide the same full model for <code class="language-plaintext highlighter-rouge">showDirectoryPicker()</code> and <code class="language-plaintext highlighter-rouge">readwrite</code> access over local folders. By contrast, Chrome/Chromium on desktop and Chrome on Android are the environments where this question becomes practically relevant.</p>

<h3 id="what-happens-under-the-hood-in-chromium">What Happens Under the Hood in Chromium</h3>

<p>The File System Access API is split across multiple layers:</p>

<ul>
  <li>The renderer process where JavaScript executes</li>
  <li>Blink/Mojo interfaces</li>
  <li>Browser-process components responsible for permissions and policy checks</li>
  <li>Permission context logic</li>
  <li>Filesystem backend services</li>
</ul>

<p>In other words, once a site receives a handle, operations pass through a full browser enforcement chain.</p>

<p>While reading Chromium source code, one of the most interesting points is how access to child entries and sensitive paths is handled. Chromium includes checks intended to prevent previously granted handles from being abused to reach problematic locations, for example via symlink traversal or a child entry that resolves to a sensitive location.</p>

<p>In those cases, sensitive-entry access checks are applied. If the result is not allowed, the operation may fail with <code class="language-plaintext highlighter-rouge">SecurityError</code>, or the entry may be filtered from results.</p>

<figure class="figure-centered">
	<img class="img-crisp" style="width: 60%; height: auto;" src="/assets/images/rogue-sandbox/Image5.png" alt="Chromium performs an additional verification before returning access to file entries that may resolve to sensitive locations" />
	<figcaption>Figure 5 - Chromium performs an additional verification before returning access to file entries that may resolve to sensitive locations</figcaption>
</figure>

<figure class="figure-centered">
	<img class="img-crisp" style="width: 60%; height: auto;" src="/assets/images/rogue-sandbox/Image6.png" alt="Chromium denies a handle when sensitive entry verification fails" />
	<figcaption>Figure 6 - Chromium denies a handle when sensitive entry verification fails.</figcaption>
</figure>

<h3 id="research-conclusion">Research Conclusion</h3>

<p>The browser does attempt to protect the user. This is not a simplistic “give me <code class="language-plaintext highlighter-rouge">C:\</code> and done” model. There are real checkpoints in the flow.</p>

<p>However, those safeguards do not eliminate the core risk. If the user selects a non-sensitive folder that still contains high-value personal or business files, the API can still enable meaningful impact inside that granted scope.</p>

<figure class="figure-centered">
	<img class="img-crisp" style="width: 40%; height: auto;" src="/assets/images/rogue-sandbox/Image7.png" alt="The error message shown to the user" />
	<figcaption>Figure 7 - The error message shown to the user</figcaption>
</figure>

<p>An important point is that the most interesting part is not only that Chromium may return <code class="language-plaintext highlighter-rouge">SecurityError</code> in some cases. The key point is the boundary between directories treated as sensitive and ordinary user directories.</p>

<p>System directories, root locations, or browser profile paths may be blocked. But user-selected directories such as Documents, Downloads, Pictures, or a project folder may remain accessible. From an organizational perspective, that is exactly where business value resides, and exactly where an attacker wants to operate: working files, documents, deliverables, presentations, images, source code, and synced cloud files.</p>

<h2 id="from-theory-to-practice">From Theory to Practice</h2>

<p>Up to this point, the article has focused primarily on the File System Access API permission model, how Chromium enforces it, and the gap between how users perceive a scan action and how the browser interprets it.</p>

<p>At some stage, however, a theoretical model is no longer enough. If a website can obtain <code class="language-plaintext highlighter-rouge">readwrite</code> access to a local directory, and the browser validates permission rather than intent, the next question is what can be done with that permission in practice.</p>

<p>The first practical stage in my research was a lab-built browser-based ransomware prototype, inspired by the Check Point research. For me, this stage served as validation: it proved that the model works, that the browser allows the flow, and that a user can reach a state in which they grant write permission over local files without feeling that they executed foreign code.</p>

<figure class="figure-centered">
	<img class="img-crisp" src="/assets/images/rogue-sandbox/Image8.png" alt="Ransomware and encryption stage in the browser-based attack flow" />
	<figcaption>Figure 8 - Ransomware and Encryption</figcaption>
</figure>

<p>For readers who want to review the ransomware validation code, the publication is available here: <a href="https://github.com/danieloz147/Browser-Based-Ransomware">Browser-Based-Ransomware</a></p>

<p>The research direction quickly evolved beyond encryption. Encryption is a dramatic outcome, but it is not the core mechanism. The core mechanism is modification of an existing file under a user-granted permission model.</p>

<p>That shift produced the practical Rogue Sandbox question: can the same trust model be used to alter existing client-side files without the user experiencing the event as an infection?</p>

<p>In simple terms:</p>

<ul>
  <li>Browser-based ransomware was the trigger.</li>
  <li>Rogue Sandbox is the model.</li>
  <li>Client-side file infection became the research direction.</li>
</ul>

<h2 id="client-side-file-infection">Client-Side File Infection</h2>

<p>The central transition in this phase was moving from file encryption to controlled modification of existing files. Instead of modeling the browser as an actor that encrypts files and displays a ransom screen, I tested a different scenario: a website that looks like a scanner, receives write permission, modifies an existing local file, and writes it back to disk as a changed file.</p>

<p>The key point is that this is not a new-file download event. The user does not receive an executable from the internet, does not open an attachment, and does not run an installer. <strong>The user selects an existing folder from their own filesystem, and the full operation remains inside the browser permission model.</strong></p>

<p>For demonstration, I used a controlled and minimal payload that only triggers a MessageBox. This choice was intentional. The objective was not to demonstrate destructive behavior, persistence, stealth, or complex payload engineering. The objective was to prove one specific point: an existing file can be modified so that the change is visible at execution time, and the chain starts with write permission granted by the user.</p>

<p>At a high level, the lab flow was:</p>

<ol>
  <li>The user opens a website presented as a scanning tool.</li>
  <li>The site asks the user to select a folder for scanning.</li>
  <li>The browser shows a legitimate folder picker.</li>
  <li>The user selects a dedicated test folder and grants <code class="language-plaintext highlighter-rouge">readwrite</code> permission.</li>
  <li>The site enumerates files in scope and identifies designated test targets.</li>
  <li>Instead of introducing a new file, the site modifies an existing file within granted scope.</li>
  <li>The modified file is written back to disk.</li>
  <li>In controlled execution of the test file, a MessageBox confirms that the change persisted.</li>
</ol>

<p>From a research perspective, this is the difference between download and trusted write-back. In the classic model, a new file enters the system from an external source. Here, a file that already exists on the system is modified through an action the user interpreted as scanning or cleanup.</p>

<p>This experiment was not meant to show arbitrary code execution by the browser. The browser does not gain unrestricted local execution, does not gain unrestricted disk access, and does not bypass Chromium isolation boundaries. The research is interesting precisely because it stays inside browser constraints.</p>

<p>The capability tested is narrower and clearer: after user approval, can a local file be read, modified in a controlled client-side process, and written back to its original location via the File System Access API?</p>

<p>That is why the technical emphasis in this section is the operation chain rather than sophisticated payload behavior:</p>

<ul>
  <li>Obtain a directory handle after explicit user approval.</li>
  <li>Identify explicitly designated test files in a lab environment.</li>
  <li>Read and analyze file structure at browser level.</li>
  <li>Apply controlled, reproducible file-content modification.</li>
  <li>Write the file back to its original location.</li>
  <li>Verify persistence of the change after browser closure.</li>
</ul>

<p>In full implementations, modification details are file-type dependent. For PE files, random byte edits are not sufficient. To keep a file valid after modification, the file structure, section boundaries, entry point behavior, and Windows loader interpretation all matter.</p>

<figure class="figure-centered">
	<img class="img-crisp" src="/assets/images/rogue-sandbox/Image9.png" alt="Just think about it" />
	<figcaption>Figure 9 - Just think about it</figcaption>
</figure>

<h2 id="technical-limitations">Technical Limitations</h2>

<p>During implementation, one point became clear quickly: client-side file infection is not trivial.</p>

<p>A browser is not a classic malware-development environment. There are memory limits, performance limits, API limits, browser-specific constraints, and full dependency on explicit user action.</p>

<p>In addition, not every file is a suitable candidate for modification. Signed files, structurally complex files, very large files, or files protected by security products may fail, be blocked, or break in a way that prevents execution. That is why the demo focused on controlled test files and a minimal payload.</p>

<p>The conclusion is not that every website can infect every file.</p>

<p>The conclusion is that the boundary between a web workflow and meaningful access to local files is narrower than many users assume.</p>

<h2 id="building-the-story-the-narrative-is-part-of-the-technique">Building the Story: The Narrative Is Part of the Technique</h2>

<p>At this point, it is important to return to a non-purely-technical point that is still critical to the research: the story shown to users is not decoration. It is part of the mechanism that enables permission.</p>

<p>The File System Access API requires user action. A picker cannot be opened silently in the background without interaction. Because of that, the entire model depends on how users are persuaded to perform a legitimate action they believe belongs to a safe process.</p>

<p>This is why I built the flow around a malware scanner narrative. Users expect such a product to touch files: read, analyze, detect, and sometimes “clean” them. The word clean creates a psychological bridge between read and write. The user thinks in terms of repair. The browser evaluates write permission.</p>

<p>The full flow was designed around several UX principles:</p>

<ul>
  <li>Browser compatibility checks early in the process to reinforce product credibility.</li>
  <li>Folder selection through native browser picker flows, not a classic upload pattern.</li>
  <li>Language centered on Scan, Analyze, Clean, and Protect rather than Modify or Write.</li>
  <li>Low, gradual threat counters to keep the experience plausible and non-theatrical.</li>
  <li>Progress bars and action logs that look like routine scanner activity.</li>
  <li>A positive completion state that signals “files are protected,” while file changes may have occurred.</li>
</ul>

<p>The point is not that the user sees nothing.</p>

<p>The opposite is true: the user sees a lot, including process states, counts, and success messaging. The concealment is not the absence of UI. The concealment is that the UI frames technical actions as a different story.</p>

<p>That is narrative hiding.</p>

<p>The user sees “cleanup”. The system sees write.
The user sees “protected files”. The file sees modification.</p>

<h2 id="injection-architecture">Injection Architecture</h2>

<p>The demonstration architecture was built around one simple principle: all meaningful processing happens client-side.</p>

<p>There is no local agent installation, no native binary execution, no browser exploit, and no JavaScript-to-native escape. The site runs inside the browser, uses the File System Access API, receives a <code class="language-plaintext highlighter-rouge">FileSystemDirectoryHandle</code> for a user-selected directory, and performs processing through JavaScript over files within granted scope.</p>

<p>In this model, the server is not the primary component. It can serve the website, support UI state, collect research telemetry, or host demo assets, but the file-modification process itself occurs in-browser.</p>

<p>Architecturally, the demo contains several layers:</p>

<ul>
  <li>Narrative layer: the website experience shown to the user.</li>
  <li>Permission layer: picker interaction and handle grant.</li>
  <li>Scanning layer: traversal of files under selected scope.</li>
  <li>Filtering layer: selection of relevant test candidates only.</li>
  <li>Binary-processing layer: file read as <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> and validation.</li>
  <li>Controlled-modification layer: deterministic change on suitable files.</li>
  <li>Write-back layer: persistence to original location using granted write permission.</li>
  <li>UI layer: action represented as scanning, cleaning, or repairing.</li>
</ul>

<h2 id="client-side-infection-pipeline">Client-Side Infection Pipeline</h2>

<p>The central implementation element is JavaScript that performs client-side processing and controlled file modification.</p>

<p>This article does not publish a full operational infection recipe. The goal here is to explain the model, the architecture, and the constraints required for a browser-contained implementation to be feasible.</p>

<p>The pipeline starts only after the user selects a folder and the browser returns a <code class="language-plaintext highlighter-rouge">FileSystemDirectoryHandle</code>. From there, code iterates entries, checks which files are relevant to the research, and skips anything outside defined criteria.</p>

<p>That filtering stage is essential. Not every file is a viable candidate. Some files do not match the required format, some are too large for practical browser-side processing, some should be excluded by policy, and some are simply irrelevant to test objectives.</p>

<p>The implementation therefore used a fail-closed approach: if a file does not satisfy predefined constraints, it is skipped.</p>

<p>In practice, filtering considered parameters such as:</p>

<ul>
  <li>File type and extension.</li>
  <li>File size thresholds.</li>
  <li>Path relationship to selected scope.</li>
  <li>Names and directories on explicit skip lists.</li>
  <li>Files already processed.</li>
  <li>Files that cannot be read or written.</li>
  <li>Files whose structure fails validation checks.</li>
  <li>Dry-run or test-mode state.</li>
</ul>

<p>Only after passing filtering is a file read into memory through its handle. At that point, processing happens over <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> or <code class="language-plaintext highlighter-rouge">Uint8Array</code>, i.e., binary representation in-browser.</p>

<p>For controlled demonstration, a MessageBox-only payload marker was used intentionally. The objective was not damage, stealth, persistence, or complexity, but a clear observable proof of behavioral change without introducing unnecessary aggression.</p>

<p>In short, MessageBox is not the end goal. It is a marker.</p>

<p>After processing, code creates a modified in-memory version and writes it back through the File System Access API itself.</p>

<p>There is no classic new-file download to Downloads, no attachment event, and no installer event. The browser uses origin-scoped write permission to open a stream and persist updated content into an existing local file.</p>

<p>This is where “no download moment” shifts from a psychological claim to a technical one.</p>

<p>From an attacker-visibility perspective, this is significant: the user did not download a new artifact. They selected a folder, initiated a scan-like workflow, and the local file changed in place.</p>

<h2 id="what-the-code-must-account-for">What the Code Must Account For</h2>

<p>The first constraint is environmental: the browser is not a natural malware-tooling environment. There is no unrestricted filesystem access, no arbitrary background execution without interaction, no full-disk visibility, and no bypass of Chromium policy boundaries.</p>

<p>Everything depends on what the user selected and what the browser agreed to expose.</p>

<p>Because of that, implementation must be defensive around handles, permission states, and errors. Any step can fail: the user can cancel selection, the browser can block sensitive directories, files can be locked, permission can remain read-only, or the API may not be supported in the current browser.</p>

<p>In the research implementation, each of these conditions had dedicated handling. That is not because it changes the core idea, but because it is what separates a theoretical concept from a functioning proof-of-concept.</p>

<p>A PoC cannot assume universal success. It must validate support, validate permissions, validate file candidates, skip exceptions safely, and preserve a stable user-facing flow even when only part of the scope is processable.</p>

<p>The second constraint is memory and performance. Browser-side binary processing relies on buffers and may involve large files. Practical implementations need size limits, staged processing, incremental UI updates, and avoidance of long main-thread blocking.</p>

<p>The third constraint is post-modification validity. If the objective is to demonstrate controlled infection, modified files need to remain valid enough to demonstrate behavior change. That means no blind writes: structure-aware processing is required to avoid immediate corruption.</p>

<figure class="figure-centered">
	<img class="img-crisp" src="/assets/images/rogue-sandbox/Image10.png" alt="Client-side infection" />
	<figcaption>Figure 10 - Client-Side Infection</figcaption>
</figure>

<p>The practical implication is that JavaScript is not “just UI” in this model.</p>

<p>It becomes a local processing layer that receives filesystem input, applies binary logic, and writes content back to disk through a legitimate browser permission boundary.</p>

<p>That is exactly where Rogue Sandbox becomes operationally meaningful.</p>

<p>The environment the user perceives as a scanner is not only presenting results. It can become a real processing environment. And once that processing environment holds write permission, the boundary between “inspection” and “infection” becomes fully dependent on the intent of code running in-browser.</p>

<h2 id="mark-of-the-web-when-you-edit-a-file-instead-of-downloading-it">Mark of the Web: When You Edit a File Instead of Downloading It</h2>

<div class="research-credit-note">
	<p class="research-credit-label">Research Credit</p>
	<p><strong><a href="https://www.linkedin.com/in/sagi-olshansky/" target="_blank" rel="noopener noreferrer">Sagi Olshansky</a></strong> raised the key Mark of the Web (MOTW) question that shaped this chapter.</p>
</div>

<p>One of the most important questions in this research was Mark of the Web (MOTW).</p>

<p>In classic Windows scenarios, when a file arrives from an untrusted source such as an internet download or email attachment, the system may mark it using <code class="language-plaintext highlighter-rouge">Zone.Identifier</code>. That marker is consumed by applications and security controls, for example by Microsoft Office when deciding whether to block macros in internet-origin files.</p>

<p>In the Rogue Sandbox scenario, the framing is different. There is not necessarily a newly downloaded file. Instead, an already-existing local file may be modified by the browser. The research question therefore became: is MOTW still relevant when the event is edit rather than download?</p>

<p>To evaluate this, I tested three separate cases:</p>

<ol>
  <li>A lab-created local file with no MOTW, then modified through the browser.</li>
  <li>An internet-origin file already marked with MOTW, then modified through the browser.</li>
  <li>A new file created or written by the browser through the File System Access API.</li>
</ol>

<p>After deeper analysis, the core conclusion is that MOTW primarily answers a provenance question based on how the file is represented in its current filesystem state. Editing by a website does not automatically reclassify a file as internet-downloaded in the Windows trust model.</p>

<figure class="figure-centered">
	<img class="img-crisp" style="width: 30%; height: auto;" src="/assets/images/rogue-sandbox/Image11.png" alt="Mark Of The Web" />
	<figcaption>Figure 11 - Mark Of The Web</figcaption>
</figure>

<h2 id="smartscreen-and-what-happens-when-there-is-no-download">SmartScreen and What Happens When There Is No Download</h2>

<p>One of the more surprising findings in this research appeared when testing write-back to existing files through the File System Access API.</p>

<p>Until that point, I treated this flow as outside the classic download-security path. The user did not download a new internet file. There was no download shelf event, no new artifact in Downloads, no <code class="language-plaintext highlighter-rouge">Content-Disposition</code> flow, and no event that looked like a traditional download.</p>

<p>At a high level, the sequence looked simple:</p>

<div class="attack-chain" role="img" aria-label="SmartScreen chapter high-level sequence from folder selection to local in-place edit">
	<span class="step">User selects a folder</span>
	<span class="arrow">→</span>
	<span class="step">Site receives write permission</span>
	<span class="arrow">→</span>
	<span class="step">Local file is edited in place</span>
</div>

<p>In practical testing against Chrome, however, the picture was more nuanced. In some cases, when the site attempted to write back specific files through the File System Access API, Chrome triggered protections associated with Safe Browsing / Download Protection.</p>

<p>This became an important point in the research because it challenged an early assumption: the absence of a classical download event does not necessarily mean the browser completely ignores file risk.</p>

<p><strong>That is exactly where this chapter becomes interesting.</strong></p>

<figure class="figure-centered">
	<img class="img-crisp" src="/assets/images/rogue-sandbox/Image12.png" alt="Chrome Safe Browsing Error" />
	<figcaption>Figure 12 - Chrome Safe Browsing Error</figcaption>
</figure>

<p>Most people associate Safe Browsing primarily with phishing pages, malicious websites, or suspicious downloads. That is indeed a central part of the mechanism. Google describes Safe Browsing as a system that warns users when they attempt to visit dangerous sites or download harmful files and applications, and Enhanced Safe Browsing can apply deeper analysis options for downloaded content.</p>

<figure class="figure-centered">
	<img class="img-crisp" src="/assets/images/rogue-sandbox/Image13.png" alt="Google's Safe Browsing" />
	<figcaption>Figure 13 - <a href="https://safebrowsing.google.com/" target="_blank" rel="noopener noreferrer">Google's Safe Browsing</a></figcaption>
</figure>

<p>Chromium also includes a dedicated path that bridges File System Access write operations into the Download Protection stack. In Chromium source code, there are dedicated components such as <code class="language-plaintext highlighter-rouge">check_file_system_access_write_request.cc</code> and <code class="language-plaintext highlighter-rouge">file_system_access_metadata.cc</code>, and in <code class="language-plaintext highlighter-rouge">download_protection_service.cc</code> there is an explicit function named <code class="language-plaintext highlighter-rouge">CheckFileSystemAccessWrite</code>.</p>

<p>In other words, from Chromium’s perspective, writing through the File System Access API is not always treated as “just file editing”. It can be elevated into an event that passes through Safe Browsing / Download Protection inspection logic.</p>

<figure class="figure-centered">
	<img class="img-crisp" style="width: 617px; max-width: 100%; height: auto;" src="/assets/images/rogue-sandbox/Image14.png" alt="CheckFileSystemAccessWrite" />
	<figcaption>Figure 14 - CheckFileSystemAccessWrite</figcaption>
</figure>

<p>At this stage, Chromium receives a <code class="language-plaintext highlighter-rouge">FileSystemAccessWriteItem</code>, checks whether the delegate allows a protection check for that write operation, and if allowed, creates a <code class="language-plaintext highlighter-rouge">CheckFileSystemAccessWriteRequest</code> and starts the inspection flow.</p>

<figure class="figure-centered">
	<img class="img-crisp" style="width: 813px; max-width: 100%; height: auto;" src="/assets/images/rogue-sandbox/Image15.png" alt="The trigger is not only the origin. Chromium also looks at the written file as an object with type, path, metadata and policy context." />
	<figcaption>Figure 15 - The trigger is not only the origin. Chromium also looks at the written file as an object with type, path, metadata and policy context.</figcaption>
</figure>

<p>As seen above, the <code class="language-plaintext highlighter-rouge">IsSupportedDownload</code> path checks whether a file type is eligible for a full inspection route and may downgrade to a narrower path when it is not. As a result, not every write-back operation receives identical treatment. Chromium considers file characteristics, classification, policy constraints, and route eligibility.</p>

<p>In my research scenario, this constraint was handled through narrative design: content was represented under a non-EXE extension, and users were socially guided to restore the extension afterward.</p>

<h2 id="chrome-vs-edge">Chrome vs. Edge</h2>

<p>Both are Chromium-based, so the File System Access API layer is very similar at the web-platform level.</p>

<p>However, the reputation and protection layer is not identical. Chrome relies on Google Safe Browsing, while Edge integrates Microsoft Defender SmartScreen. Microsoft documents SmartScreen as protection against phishing/malware sites, suspicious applications, and potentially malicious downloads; Edge documentation also notes that relevant URL/file signals can be sent to SmartScreen services for reputation checks.</p>

<p>In my lab tests, Edge did not block folder-file access and editing in the same way as Chrome. Because of that, the client-side logic included browser-aware branching with a different narrative model per browser capability.</p>

<p>The research meaning is that the primitive is the same, but defensive response can differ. The same workflow can pass through different decision engines because each browser wraps Chromium with its own reputation, policy, and security UX layers.</p>

<h2 id="demo">Demo</h2>

<div class="post-video">
	<iframe src="https://www.youtube.com/embed/r4HU3-r76Do?rel=0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>

<div class="post-video">
	<iframe src="https://www.youtube.com/embed/kNLB0Ax0TPc?rel=0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>

<p>Note that both videos show Rogue Sandbox.</p>

<p>In the first demo, executable-file editing is demonstrated through Microsoft <code class="language-plaintext highlighter-rouge">Edge</code> browser in a controlled lab context.
In the second demo, executable-file editing is demonstrated through Google <code class="language-plaintext highlighter-rouge">Chrome</code> browser with a non-protected extension narrative and controlled follow-up flow.</p>

<p>For convenience, the project is available here for review and feedback: <a href="https://github.com/danieloz147/rogue-sandbox">Rogue Sandbox project repository</a></p>

<h2 id="what-can-be-done-defensively">What Can Be Done Defensively?</h2>

<p>This is offensive research, but the defensive implications are unavoidable. If a browser can obtain write permission to a local folder and modify existing files under legitimate user consent, organizations should treat File System Access API as a permission surface with real security impact.</p>

<p>The point is not to block browsers entirely. File System Access API is legitimate and useful. The problem is that many organizations still model browsers mainly as upload/download endpoints, not as local-file editors.</p>

<h3 id="organizational-recommendations">Organizational Recommendations</h3>

<ul>
  <li>Configure browser policy to limit or control site requests for read/write filesystem access.</li>
  <li>Allow File System Access only for known and approved origins where business need is explicit.</li>
  <li>Train users that folder permission is not equivalent to normal upload; it is permission to operate on local files.</li>
  <li>Monitor browser processes writing to many files or changing sensitive extensions.</li>
  <li>Treat file modifications by <code class="language-plaintext highlighter-rouge">chrome.exe</code> or <code class="language-plaintext highlighter-rouge">msedge.exe</code> as behavioral signals that require context.</li>
</ul>

<p>Additionally, both Chrome and Edge provide enterprise policies to control how sites request filesystem read/write access. In organizations without a clear business requirement, blocking or tight allowlisting is worth considering.</p>

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

<p>Rogue Sandbox is not a sandbox-escape story. The browser did not break its boundaries. It operated exactly as designed: user gesture, folder selection, permission grant, and origin-scoped capability.</p>

<p>The issue is that users and browsers interpret the same event differently. The user sees scanning, cleanup, and protection. The browser sees handle, permission, and write. Rogue Sandbox lives in that interpretation gap.</p>

<p>The research started from browser-based encryption concepts and moved into a broader direction: if browser-mediated encryption is possible under user-granted scope, other controlled file modifications are possible too, including client-side infection demonstrations in lab settings.</p>

<p>The novelty is not that browsers are inherently “dangerous”. The novelty is that browsers have become local execution-adjacent environments driven by user-granted permissions. When credible narrative drives write consent, the scanning environment itself can become the payload path.</p>

<p>The user did not download a new file. The user allowed a site to edit a file that was already present. That is the distinction many defense models still fail to account for.</p>

<h2 id="research-notes">Research Notes</h2>

<p>This section captures important observations that did not fit the main flow but are worth preserving.</p>

<h3 id="safari-and-firefox">Safari and Firefox</h3>

<p>The full <code class="language-plaintext highlighter-rouge">showDirectoryPicker()</code> + local-folder <code class="language-plaintext highlighter-rouge">readwrite</code> model is not supported in the same way across all browsers. Safari and Firefox do not currently expose the same operational surface as Chromium in this context.</p>

<h3 id="chrome-on-android">Chrome on Android</h3>

<p>Check Point highlighted Chrome on Android relevance for media-folder scenarios. This research focuses mainly on desktop because controlled modification of existing executable formats is more relevant to Windows desktop workflows.</p>

<h3 id="sensitive-directories">Sensitive Directories</h3>

<p>Chromium does not provide unrestricted path access. It includes checks for sensitive directories and entries, including symlink-to-blocked-location scenarios. This model does not bypass those checks; it leverages user-selected, non-blocked areas.</p>

<h3 id="startin-and-full-system-access"><code class="language-plaintext highlighter-rouge">startIn</code> and Full-System Access</h3>

<p><code class="language-plaintext highlighter-rouge">startIn</code> does not grant root or full-system access. It only influences picker starting location. Actual access remains dependent on user choice and browser enforcement.</p>

<h3 id="user-gesture">User Gesture</h3>

<p>The user-gesture requirement is important but does not prevent social engineering. If users click Scan within a believable narrative, the technical requirement is satisfied.</p>

<h3 id="cloud-sync">Cloud Sync</h3>

<p>A relevant future direction is synced folders such as OneDrive, Google Drive, or Dropbox. Browser-mediated local edits may be propagated by sync clients. This was not the primary attack case here, but it is an important follow-up direction.</p>

<h3 id="digital-signatures">Digital Signatures</h3>

<p>Modifying signed files is expected to break signatures. This can become both a detection signal and a technical limitation. The current demo does not rely on signature bypass.</p>

<h3 id="edr-visibility">EDR Visibility</h3>

<p>EDR products may observe <code class="language-plaintext highlighter-rouge">chrome.exe</code> or <code class="language-plaintext highlighter-rouge">msedge.exe</code> writing local files. However, absent rapid high-volume file churn, this activity may remain low-noise and under-classified.</p>

<h3 id="safe-browsing-is-not-only-phishing">Safe Browsing Is Not Only Phishing</h3>

<p>The research indicates that Safe Browsing relevance extends beyond phishing-page and classical-download scenarios. Chromium includes dedicated write-inspection paths for File System Access operations, but behavior still depends on file type, classification, and browser policy.</p>

<h2 id="sources">Sources</h2>

<ul>
  <li>Check Point Research - Browser-Only Ransomware: From LLM Hallucinations to a Practical Attack Technique<br />
  https://research.checkpoint.com/2026/browser-only-ransomware-from-llm-hallucinations-to-a-practical-attack-technique/</li>
  <li>Chrome Developers - The File System Access API<br />
  https://developer.chrome.com/docs/capabilities/web-apis/file-system-access</li>
  <li>MDN - File System API<br />
  https://developer.mozilla.org/en-US/docs/Web/API/File_System_API</li>
  <li>Chromium source - file_system_access_directory_handle_impl.cc<br />
  https://chromium.googlesource.com/chromium/src/+/main/content/browser/file_system_access/file_system_access_directory_handle_impl.cc</li>
  <li>WICG - File System Access specification<br />
  https://wicg.github.io/file-system-access/</li>
  <li>Microsoft Learn - Macros from the internet are blocked by default in Office<br />
  https://learn.microsoft.com/en-us/microsoft-365-apps/security/internet-macros-blocked</li>
  <li>Microsoft Learn - Microsoft Edge support for Microsoft Defender SmartScreen<br />
  https://learn.microsoft.com/en-us/deployedge/microsoft-edge-security-smartscreen</li>
  <li>Chrome Enterprise Policy - DefaultFileSystemWriteGuardSetting<br />
  https://chromeenterprise.google/policies/default-file-system-write-guard-setting/</li>
  <li>Microsoft Edge Policy - DefaultFileSystemWriteGuardSetting<br />
  https://learn.microsoft.com/en-us/deployedge/microsoft-edge-policies/defaultfilesystemwriteguardsetting</li>
</ul>]]></content><author><name>Daniel Oz</name></author><category term="browser-security" /><category term="chromium" /><category term="file-system-access" /><category term="MOTW" /><category term="research" /><category term="red-team" /><summary type="html"><![CDATA[Research on Rogue Sandbox: how an apparently benign scan flow can become browser-granted write access and reshape the threat model without an exploit or local payload.]]></summary></entry><entry><title type="html">Exploiting the iOS Trust Model via Configuration Profiles</title><link href="https://danieloz147.github.io/2026/06/26/exploiting-ios-trust-model-via-configuration-profiles/" rel="alternate" type="text/html" title="Exploiting the iOS Trust Model via Configuration Profiles" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://danieloz147.github.io/2026/06/26/exploiting-ios-trust-model-via-configuration-profiles</id><content type="html" xml:base="https://danieloz147.github.io/2026/06/26/exploiting-ios-trust-model-via-configuration-profiles/"><![CDATA[<h2 id="tldr">TL;DR</h2>

<ul>
  <li>You don’t always need a vulnerability to impact an iPhone. In many scenarios, social engineering plus legitimate iOS features is enough.</li>
  <li>Configuration Profiles can expand device trust with explicit user approval, while the device still appears “secure” and uncompromised.</li>
  <li>Malicious profile scenarios include forced trust changes (Wi-Fi, certificates, VPN/proxy/MDM settings) that alter traffic visibility and exposure.</li>
  <li>The core risk is not a broken security boundary, but user-approved reconfiguration of trust.</li>
  <li>Defense should focus on profile governance, user awareness, and continuous monitoring for unauthorized profile/certificate/MDM changes.</li>
</ul>

<h2 id="abstract" class="post-h2-center post-h2-emphasis">Abstract</h2>

<p>iOS is considered one of the most secure mobile operating systems in the world. Over the years, Apple has built many layers of protection, such as Code Signing, Sandbox, hardware-based security mechanisms, and a relatively closed app distribution model. As a result, when talking about “breaking into an iPhone”, the discussion usually revolves around 0-Day vulnerabilities, complex exploitation chains, Jailbreaks, or advanced spyware of the kind typically associated with state actors or commercial companies with significant resources.</p>

<p>But what if you don’t need to break in at all?</p>

<p>In many cases, an attacker doesn’t need to bypass security mechanisms, exploit a vulnerability, or execute malicious code. It is enough to get the user to give the device an instruction to do exactly what it was designed to do.</p>

<p>This article focuses on one of the most interesting mechanisms in this context - Configuration Profiles, Certificates, MDM components, and additional system settings - which can significantly alter the device’s trust model and its exposure level, without the device being considered “compromised” in the conventional sense of the word, and while still operating entirely within the legitimate mechanisms of the operating system.</p>

<p>Throughout the article, we will examine how configuration profiles can be combined with social engineering techniques, what effects this may have on the user, how such scenarios can be identified, and what can be done to defend against them.</p>

<p>Ultimately, the central argument of the article is fairly simple. Sometimes the easiest way to affect an iPhone is not to bypass its security mechanisms - but to use them exactly as Apple intended. What is known in professional parlance as: “it’s not a bug, it’s a feature”.</p>

<hr />

<figure class="figure-centered">
  <img src="/assets/images/ios-trust/image1.png" alt="Figure 1" />
  <figcaption>Figure 1 - The entire article in one image</figcaption>
</figure>

<hr />

<nav class="post-toc" aria-label="Table of contents">
  <h3>Table of Contents</h3>
  <ol>
    <li><a href="#abstract">Abstract</a></li>
    <li><a href="#background-and-motivation">Background and Motivation</a></li>
    <li>
      <a href="#the-ios-trust-model">The iOS Trust Model</a>
      <ol>
        <li><a href="#so-what-are-configuration-profiles-exactly">So What Are Configuration Profiles, Exactly?</a></li>
        <li><a href="#signed-or-not-signed-that-is-the-question">Signed or Not Signed? That is the Question</a></li>
        <li><a href="#anatomy-of-a-configuration-profile">Anatomy of a Configuration Profile</a></li>
        <li><a href="#so-how-do-you-actually-create-a-configuration-profile">So How Do You Actually Create a Configuration Profile?</a></li>
      </ol>
    </li>
    <li>
      <a href="#the-many-faces-of-trust-me-bro">The Many Faces of "Trust Me Bro"</a>
      <ol>
        <li><a href="#trust-me-bro-its-our-app">Trust Me Bro, It's Our App</a></li>
        <li><a href="#trust-me-bro-its-the-company-wi-fi">Trust Me Bro, It's The Company Wi-Fi</a></li>
        <li><a href="#trust-me-bro-route-everything-through-me">Trust Me Bro, Route Everything Through Me</a></li>
        <li><a href="#trust-me-bro-im-it">Trust Me Bro, I'm IT</a></li>
      </ol>
    </li>
    <li><a href="#what-comes-next">What Comes Next</a></li>
    <li><a href="#references">References</a></li>
  </ol>
</nav>

<h2 id="background-and-motivation" class="post-h2-center post-h2-emphasis">Background and Motivation</h2>

<p>The smartphone has long since become far more than a communication device. It contains messages, photos, email, financial information, authentication factors (MFA), location history, and sometimes even direct access to corporate systems. For attackers, access to a mobile device can therefore be particularly valuable. In many cases, the phone is not the final target but rather the entry point (Initial Access) - at least to some degree.</p>

<p>When talking about “breaking into an iPhone”, the conversation typically centers around 0-day-level exploitation and advanced spyware. Not by coincidence - iOS is considered one of the most secure operating systems in the world, and Apple invests enormous resources in hardening it. As a result, much of the research and public discussion focuses on the question of how to bypass its protection mechanisms in one way or another.</p>

<p>But… what if you don’t need to exploit a vulnerability at all?</p>

<p>The purpose of this article is not to deal with memory vulnerabilities, bypasses, or sophisticated exploits. Instead, we will examine a much simpler question - can significant impact be achieved against an iPhone without exploiting a vulnerability, without bypassing a security mechanism, and without compromising the operating system?</p>

<p>As we will see, the answer has less to do with lines of code and more to do with the relationship between the user and the device. After all, iOS is built on a strict trust model.</p>

<p>But there is one factor that operates outside those hardening mechanisms.</p>

<p>A factor that almost always makes the final decision.</p>

<p><strong>The user.</strong></p>

<blockquote>
  <p><strong>Author’s note:</strong> During the writing of this article, no Apple security mechanisms were harmed. A number of hypothetical users, however, did click the Install button without reading a single one of the warnings presented to them.</p>
</blockquote>

<hr />

<figure class="figure-centered">
  <img src="/assets/images/ios-trust/image2.png" alt="Figure 2" />
  <figcaption>Figure 2 - Sometimes the easiest way to bypass a security mechanism is to not bypass it at all</figcaption>
</figure>

<hr />

<h2 id="the-ios-trust-model" class="post-h2-center post-h2-emphasis">The iOS Trust Model</h2>

<p>Before discussing Configuration Profiles, MDM, or what certificates actually are, one fundamental concept needs to be understood - most security mechanisms in iOS are designed to prevent applications, files, or external attackers from performing actions that have not been authorized by the operating system. But what happens when the action was authorized by the user themselves?</p>

<p>If I had to describe a relatively conceptual “diagram” of the general trust model that the iPhone’s operating system relies on, I would describe it roughly like this:</p>

<hr />

<figure>
  <img src="/assets/images/ios-trust/image3.png" alt="Figure 3" class="img-white-bg" />
  <figcaption>Figure 3 - Conceptual diagram of the iOS security layers</figcaption>
</figure>

<hr />

<h3 id="the-three-layers-base-to-top">The Three Layers (Base to Top)</h3>

<p><strong>The Purple Layer - Root of Trust:</strong>
This layer represents the device’s hardware root of trust. Mechanisms such as the Secure Enclave, Secure Boot, and cryptographic key management are designed to ensure the device boots from a known and unmodified trusted state.</p>

<p><strong>The Green Layer - Trust Enforcement:</strong>
This is where mechanisms such as Code Signing, Sandbox, Trust Store, and a complete access permission model reside. Their role is to ensure that applications and code cannot perform operations the system has not pre-approved, and that unauthorized code cannot run or gain access to system resources.</p>

<p><strong>The Orange Layer - An Exception, but a Controlled One:</strong>
This is where the story of this article begins.
iOS’s model is Default Deny, but in certain cases Apple allows the user to expand the device’s circle of trust. This expansion does not break the security mechanisms and does not bypass them - it operates through them, with explicit user approval. This expansion is carried out using Configuration Profiles.
In other words, most iOS security mechanisms are designed to restrict what code can do. Configuration Profiles are interesting for a different reason - <strong>they allow the user to add components, settings, and identities that the system would not have trusted by default</strong>.</p>

<hr />

<h2 id="so-what-are-configuration-profiles-exactly" class="post-h2-center">So What Are Configuration Profiles, Exactly?</h2>

<figure>
  <img src="/assets/images/ios-trust/image4.png" alt="Figure 4" />
  <figcaption>Figure 4 - Most iPhone users will never reach this screen. Some who do don't really know why.</figcaption>
</figure>

<p>For most iPhone users, this is one of the least familiar features in the operating system. Many users will never encounter it during their device’s lifetime. Those who do typically belong to one of two groups:</p>

<ol>
  <li>Employees in organizations that use MDM systems.</li>
  <li>People who clicked a link they weren’t entirely sure, in hindsight, they should have clicked.</li>
</ol>

<p>Configuration Profiles are not a security mechanism - practically speaking, they were born out of an operational need. When organizations began adopting iPhones at scale, a problem emerged: how do you configure hundreds or thousands of devices without going through each one manually?</p>

<p>A typical organization needs to configure for its employees:</p>

<ul>
  <li>Corporate email accounts</li>
  <li>Digital certificates</li>
  <li>VPN connections</li>
  <li>Wi-Fi networks</li>
  <li>Security restrictions</li>
  <li>Password policies</li>
  <li>Additional organizational settings</li>
</ul>

<p>Performing all of this manually for thousands of employees is not practical. To solve this problem, Apple introduced a mechanism called Configuration Profiles. Instead of asking the user to configure each component individually, a file can be sent to the target device that tells the operating system exactly which settings to apply.</p>

<p>Technically, a Configuration Profile is a signed (or unsigned) text file with a <code class="language-plaintext highlighter-rouge">.mobileconfig</code> extension. It is an Apple Property List in XML format that represents various Payloads - where each Payload can be thought of as a single “configuration unit”.</p>

<p>For example:</p>

<div class="tree">
  <div class="tree-root">Profile</div>
  <ul class="tree-children">
    <li>Wi-Fi Settings</li>
    <li>VPN Settings</li>
    <li>Certificate Settings</li>
    <li>Hardening</li>
    <li>and many more...</li>
  </ul>
</div>

<p>Each setting is responsible for a different component of the system. Behind the scenes, from the operating system’s perspective, a Configuration Profile is essentially a list of instructions to execute. For example:</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>PayloadContent<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;array&gt;</span>
        <span class="nt">&lt;dict&gt;</span>
            <span class="nt">&lt;key&gt;</span>AutoJoin<span class="nt">&lt;/key&gt;</span>
            <span class="nt">&lt;true/&gt;</span>
            <span class="nt">&lt;key&gt;</span>EncryptionType<span class="nt">&lt;/key&gt;</span>
            <span class="nt">&lt;string&gt;</span>WPA2<span class="nt">&lt;/string&gt;</span>
            <span class="nt">&lt;key&gt;</span>HIDDEN_NETWORK<span class="nt">&lt;/key&gt;</span>
            <span class="nt">&lt;false/&gt;</span>
            <span class="nt">&lt;key&gt;</span>Password<span class="nt">&lt;/key&gt;</span>
            <span class="nt">&lt;string&gt;</span>[WIFI PASSWORD TRUNCATED]<span class="nt">&lt;/string&gt;</span>
            <span class="nt">&lt;key&gt;</span>PayloadDisplayName<span class="nt">&lt;/key&gt;</span>
            <span class="nt">&lt;string&gt;</span>Wi-Fi<span class="nt">&lt;/string&gt;</span>
            <span class="nt">&lt;key&gt;</span>PayloadIdentifier<span class="nt">&lt;/key&gt;</span>
            <span class="nt">&lt;string&gt;</span>com.example.profile.wifi.0<span class="nt">&lt;/string&gt;</span>
            <span class="nt">&lt;key&gt;</span>PayloadType<span class="nt">&lt;/key&gt;</span>
            <span class="nt">&lt;string&gt;</span>com.apple.wifi.managed<span class="nt">&lt;/string&gt;</span>
            <span class="nt">&lt;key&gt;</span>PayloadUUID<span class="nt">&lt;/key&gt;</span>
            <span class="nt">&lt;string&gt;</span>B1E2F89C-C8BF-4F4E-8B05-4F19E618A368<span class="nt">&lt;/string&gt;</span>
            <span class="nt">&lt;key&gt;</span>PayloadVersion<span class="nt">&lt;/key&gt;</span>
            <span class="nt">&lt;integer&gt;</span>1<span class="nt">&lt;/integer&gt;</span>
            <span class="nt">&lt;key&gt;</span>SSID_STR<span class="nt">&lt;/key&gt;</span>
            <span class="nt">&lt;string&gt;</span>[SSID NAME TRUNCATED]<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;/dict&gt;</span>
    <span class="nt">&lt;/array&gt;</span>
    <span class="nt">&lt;key&gt;</span>PayloadDisplayName<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>Configuration Profile<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>PayloadIdentifier<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>com.example.profile<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>PayloadOrganization<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>OrgName<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>PayloadType<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>Configuration<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>PayloadUUID<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>8DA0EF1E-6AD4-4B68-A2BB-EDA3E55067E2<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>PayloadVersion<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;integer&gt;</span>1<span class="nt">&lt;/integer&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p><strong>Technical breakdown of what we see here:</strong></p>

<p>What is shown above is a Configuration Profile in <code class="language-plaintext highlighter-rouge">.mobileconfig</code> format - in practice, an Apple Property List in XML format that defines a configuration profile for an iOS device. The settings are written in Key-Value format.</p>

<p>In this case, the profile contains a single Payload - a managed Wi-Fi setting.</p>

<p>The most important part is:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;key&gt;</span>PayloadType<span class="nt">&lt;/key&gt;</span>
<span class="nt">&lt;string&gt;</span>com.apple.wifi.managed<span class="nt">&lt;/string&gt;</span>
</code></pre></div></div>

<p>This tells iOS that this is a Wi-Fi setting the system should add to the device. After installing the profile, the device will recognize a specific Wi-Fi network - including its name, encryption type, and password.</p>

<p>Structurally, there are two layers:</p>

<div class="tree">
  <div class="tree-root">Configuration Profile</div>
  <ul class="tree-children">
    <li>Payload Content
      <ul class="tree-children">
        <li>Wi-Fi Payload</li>
      </ul>
    </li>
  </ul>
</div>

<p>The outer layer is the profile itself (<code class="language-plaintext highlighter-rouge">PayloadType: Configuration</code>) - a general wrapper indicating this is a configuration profile. Inside it is <code class="language-plaintext highlighter-rouge">PayloadContent</code>, an array of Payloads. In this case there is only one, but theoretically the same profile can contain multiple Payloads together.</p>

<p>Within the Wi-Fi Payload:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">SSID_STR</code> - the name of the network the device will connect to</li>
  <li><code class="language-plaintext highlighter-rouge">Password</code> - the network password. In a real profile, the password appears as part of the file in plaintext, making it highly sensitive information. Anyone holding the <code class="language-plaintext highlighter-rouge">.mobileconfig</code> file may be able to see the password if the profile is not properly protected or encrypted</li>
  <li><code class="language-plaintext highlighter-rouge">EncryptionType: WPA2</code> - the network’s security type</li>
  <li><code class="language-plaintext highlighter-rouge">AutoJoin: true</code> - the device will automatically attempt to connect to the network when it is available</li>
  <li><code class="language-plaintext highlighter-rouge">HIDDEN_NETWORK: false</code> - the network is not hidden, meaning the SSID is broadcast normally</li>
  <li><code class="language-plaintext highlighter-rouge">PayloadIdentifier</code> / <code class="language-plaintext highlighter-rouge">PayloadUUID</code> / <code class="language-plaintext highlighter-rouge">PayloadVersion</code> - Apple metadata used to identify and manage the Payload</li>
</ul>

<p>At this stage, this is still a relatively benign example. All we did was ask the operating system to save a network and connect to it automatically. But once you understand that the user can cause the device to accept new network configuration settings, it becomes interesting to ask what that decision means in the real world.</p>

<p><strong>The key takeaway:</strong> the user is not typing a Wi-Fi password. They are installing a profile that tells the device how to behave toward a specific network. And that is the interesting difference:</p>

<ul>
  <li>In a legitimate scenario - an organization distributes Wi-Fi settings to employees.</li>
  <li>In a scenario exploited by an attacker - they can cause a user to install a profile that looks like “Conference Wi-Fi” or “Secure Network Access,” and in practice cause devices to connect to a Rogue AP. From there, a range of attack types becomes possible:</li>
</ul>

<p><strong>Visibility</strong> - the device connects to a network under someone else’s control. Consider the intelligence value of knowing:</p>
<ul>
  <li>When the user arrived</li>
  <li>How long they stayed</li>
  <li>How many devices connected</li>
  <li>Which services the device is trying to reach</li>
</ul>

<p><strong>Attack Surface Discovery</strong> - when a device is on a given network, it may expose services accessible only via Wi-Fi (depending on organizational configuration):</p>
<ul>
  <li>Device names</li>
  <li>Service Discovery</li>
  <li>DNS requests</li>
  <li>Internal domains</li>
  <li>Network topology clues</li>
</ul>

<p><strong>Credential Harvesting Opportunities</strong> - additional attacks can be launched to extract information from the victim:</p>
<ul>
  <li>Captive Portals</li>
  <li>Fake Login Pages</li>
</ul>

<p><strong>Dangerous combination with additional Payloads</strong> - Wi-Fi is one specific Payload, but it can potentially be combined with other Payload types to create a more complex attack chain:</p>

<div class="attack-chain">
  <span class="step">Wi-Fi auto connect</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Root CA installation</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Fake Corporate Portal</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Credentials collection</span>
  <span class="arrow">&#8594;</span>
  <span class="step">MITM with TLS inspection</span>
  <span class="chain-note">effectiveness depends on Certificate Pinning implementation</span>
</div>

<p>It is important to remember that behind an innocent Install button lies a direct instruction to the operating system: “Add this network, save the password, and connect to it automatically when you see it”. On their own, Wi-Fi Payloads do not grant an attacker access to the device and do not constitute a breach of the operating system. However, their combination with additional Payloads allows an attacker to influence both the device’s communication environment and its circle of trust. In such a state, it becomes possible to create an environment that appears completely legitimate to the user - including corporate portals, authentication services, and various access interfaces - without triggering the familiar warning mechanisms the user expects to see when accessing an untrusted site.</p>

<p>In this sense, the Certificate is not the target - it is the tool that makes the rest of the chain appear trustworthy. Not control over the device, but the ability to make the user stop questioning what they see. That is already a significantly more meaningful impact than just another Wi-Fi connection.</p>

<figure>
  <img src="/assets/images/ios-trust/image5.jpeg" alt="Figure 5" />
  <figcaption>Figure 5 - Even iOS tries to hint at you</figcaption>
</figure>

<hr />

<h2 id="signed-or-not-signed-that-is-the-question" class="post-h2-center">Signed or Not Signed? That is the Question</h2>

<p>One of the first questions that comes up is whether a Configuration Profile has to be signed.</p>

<p>The short answer is: <strong>No.</strong></p>

<p>iOS allows the installation of unsigned profiles as well. While a signed profile allows the operating system to verify its origin and integrity, an unsigned profile will only display an appropriate warning to the user during the installation process.</p>

<p>It is important to understand - the warning does not prevent installation, it only informs the user of the risk in a general and abstract way. As we will see, this can even be exploited.</p>

<p>At this point I want to clarify an important distinction: a profile is not Malware, it is not ShellCode, not a RootKit, and not an Exploit. It is simply a relatively automated way to perform actions through official, documented mechanisms of the operating system. This is also why using it is so interesting - it does not attempt to bypass iOS’s security model, but rather operates within it.</p>

<p><strong>So where is the problem?</strong></p>

<p>From the operating system’s perspective, the process is straightforward:</p>

<ol>
  <li>The user received a profile.</li>
  <li>The user chose to install it.</li>
  <li>The user read the warnings.</li>
  <li>The user approved the action.</li>
  <li>The profile was installed.</li>
</ol>

<p>From the attacker’s perspective, the process looks slightly different:</p>

<ol>
  <li>Get the user to receive a profile.</li>
  <li>Get the user to install it.</li>
  <li>The fun begins.</li>
</ol>

<p>The rest is handled by the operating system.</p>

<figure>
  <img src="/assets/images/ios-trust/image6.png" alt="Figure 6" />
  <figcaption>Figure 6 - Distribution vectors for a malicious profile</figcaption>
</figure>

<hr />

<h2 id="anatomy-of-a-configuration-profile" class="post-h2-center">Anatomy of a Configuration Profile</h2>

<p>Before we try to convince a user to install a profile, it is worth understanding exactly what we are installing. Behind the “Install Profile” screen hides, at the end of the day, a fairly boring XML file. And as with many stories in the cyber world, the boring things are usually the most interesting.</p>

<p>Not all Payloads are equally interesting. Some are designed for convenience, some for management, and some can significantly alter the device’s trust model.</p>

<p>The following table details the possible Payload types available, as of the time of writing, through Apple. It is not intended for studying each Payload individually - its purpose is to illustrate the breadth of the Configuration Profiles mechanism and the variety of system components that can be configured through it.</p>

<div class="payload-table-wrap">
<table class="payload-table">
<thead>
<tr>
  <th>Payload</th>
  <th>General Purpose</th>
  <th>Payload Type</th>
  <th>Why It Is Used</th>
  <th>Why It's Interesting (Attacker / Red Team)</th>
</tr>
</thead>
<tbody>
<tr><td>Wi-Fi</td><td>Configure wireless networks (SSID, EAP, WPA2/3, Hotspot 2.0)</td><td>com.apple.wifi.managed</td><td>Auto-join corporate/known networks without manual setup</td><td>Push a rogue/auto-join SSID; preload attacker EAP server to harvest 802.1X creds; force connection to a controlled AP</td></tr>
<tr><td>Ethernet</td><td>Wired network config</td><td>com.apple.*ethernet*</td><td>Managed wired endpoints</td><td>Same MITM logic as Wi-Fi on wired adapters</td></tr>
<tr><td>VPN</td><td>Device-wide VPN (IKEv2/IPsec/L2TP)</td><td>com.apple.vpn.managed</td><td>Tunnel traffic to corporate network</td><td>On-Demand VPN = persistence + full traffic redirection through attacker infra</td></tr>
<tr><td>App-Layer VPN</td><td>Per-app VPN</td><td>com.apple.vpn.managed.applayer</td><td>Scope VPN to specific managed apps</td><td>Selective interception of targeted app traffic, harder to notice</td></tr>
<tr><td>Global HTTP Proxy <span class="supervised">★ supervised</span></td><td>Force all HTTP(S) through a proxy</td><td>com.apple.proxy.http.global</td><td>Org-wide content control</td><td>Classic MITM: routes all web traffic through attacker proxy</td></tr>
<tr><td>DNS Settings / DNS Proxy</td><td>Set DNS resolver / DoH/DoT</td><td>com.apple.dnsSettings.managed / com.apple.dnsProxy.managed</td><td>Enterprise DNS, filtering</td><td>Redirect name resolution, silent MITM, covert exfil channel via DNS</td></tr>
<tr><td>Relay</td><td>Route traffic via a network relay</td><td>com.apple.relay.managed</td><td>Zero-trust network access</td><td>Insert attacker relay into the traffic path</td></tr>
<tr><td>Web Content Filter</td><td>Filter/inspect web traffic</td><td>com.apple.webcontent-filter</td><td>Parental/enterprise filtering</td><td>Inspect or redirect browsing; basis for surveillance</td></tr>
<tr><td>Domains / Associated Domains</td><td>Declare managed/associated domains</td><td>com.apple.domains / com.apple.associated-domains</td><td>Universal links, password autofill</td><td>Hijack universal-link routing or autofill association to attacker domain</td></tr>
<tr><td>Certificates (incl. Root)</td><td>Install CA/identity certs</td><td>com.apple.security.root / .pkcs12 / .pem / .pkcs1</td><td>Trust internal PKI, client auth</td><td>Install attacker root CA → full TLS interception if user grants full trust; the core of the "user-consent override" risk</td></tr>
<tr><td>SCEP</td><td>Auto-enroll device cert via SCEP</td><td>com.apple.security.scep</td><td>Scalable cert issuance</td><td>Enroll device identity into attacker PKI; foothold for mutual-TLS access</td></tr>
<tr><td>ACME</td><td>Modern automated cert enrollment</td><td>com.apple.security.acme</td><td>Replacement for SCEP, attested keys</td><td>Same enrollment-abuse surface as SCEP</td></tr>
<tr><td>Certificate Revocation / Transparency</td><td>Tune OCSP/CRL and CT policy</td><td>com.apple.security.certificaterevocation / .certificatetransparency</td><td>Control cert validation behavior</td><td>Weaken revocation checking to keep a malicious cert alive</td></tr>
<tr><td>Passcode</td><td>Enforce passcode policy</td><td>com.apple.mobiledevice.passwordpolicy</td><td>Security baseline</td><td>Weaken policy, or force a lockout (DoS); read posture for targeting</td></tr>
<tr><td>Restrictions</td><td>Large set of feature toggles</td><td>com.apple.applicationaccess</td><td>Disable camera, app installs, iCloud, Safari, etc.</td><td>Degrade security posture or lock the device down maliciously</td></tr>
<tr><td>Single Sign-On / Extensible SSO <span class="supervised">★ supervised</span></td><td>Configure SSO providers</td><td>com.apple.sso / com.apple.extensiblesso</td><td>Seamless auth to internal apps</td><td>Insert attacker IdP/extension into the auth path - credential/token interception</td></tr>
<tr><td>Mail</td><td>Configure mail accounts</td><td>com.apple.mail.managed</td><td>Provision corporate mail</td><td>Point mail to attacker server; harvest/relay messages</td></tr>
<tr><td>Exchange ActiveSync</td><td>Exchange account setup</td><td>com.apple.eas.account</td><td>Corporate email/calendar</td><td>Same mail-redirection/credential surface</td></tr>
<tr><td>Google Accounts</td><td>Google OAuth account</td><td>com.apple.google-oauth</td><td>Provision Workspace</td><td>OAuth phishing / token-grab framing</td></tr>
<tr><td>Calendar / Contacts / LDAP</td><td>PIM accounts &amp; directory</td><td>com.apple.caldav.account / .carddav.account / .ldap.account</td><td>Provision directory &amp; PIM</td><td>Data exfil, recon of org structure, attacker-controlled directory</td></tr>
<tr><td>Web Clips</td><td>Add home-screen web shortcut</td><td>com.apple.webClip.managed</td><td>Quick-launch web apps</td><td>Plant a fake "app" icon - phishing launcher that looks native</td></tr>
<tr><td>App Lock <span class="supervised">★ supervised</span></td><td>Lock device to one app</td><td>com.apple.app.lock</td><td>Kiosk mode</td><td>Forced kiosk = targeted DoS / lockout</td></tr>
<tr><td>Home Screen Layout <span class="supervised">★ supervised</span></td><td>Force icon layout</td><td>com.apple.homescreenlayout</td><td>Standardize devices</td><td>Hide/relocate icons for social engineering</td></tr>
<tr><td>Lock Screen Message <span class="supervised">★ supervised</span></td><td>Set lock-screen text</td><td>com.apple.shareddeviceconfiguration</td><td>Asset tagging</td><td>Spoof "official" messaging / fake recovery instructions</td></tr>
<tr><td>Setup Assistant</td><td>Skip/control setup panes</td><td>com.apple.SetupAssistant.managed</td><td>Streamline onboarding</td><td>Skip security/privacy prompts during provisioning</td></tr>
<tr><td>Notifications <span class="supervised">★ supervised</span></td><td>Per-app notification policy</td><td>com.apple.notificationsettings</td><td>Manage alerts</td><td>Suppress security-app alerts to hide activity</td></tr>
<tr><td>AirPlay / AirPrint / Fonts</td><td>Misc device services</td><td>com.apple.airplay / .airprint / .font</td><td>Convenience config</td><td>AirPlay/AirPrint can add attacker endpoints; font payload is a minor file-delivery vector</td></tr>
</tbody>
</table>
</div>

<p>One thing worth noting: not all Payloads in the table are available in every scenario. Some require Supervision - a special state activated through Automated Device Enrollment (ADE) or Apple Configurator with a physical connection to the device. <span class="supervised">★ supervised</span> marks these in the table above.</p>

<p>The practical implication is that an attacker distributing a Configuration Profile to a personal device does not automatically gain access to these capabilities. In this sense, the Supervision requirement serves as an additional layer of protection and significantly limits the scope of actions that can be performed via a manually installed profile.</p>

<p>It is important to note that the Supervision requirement does not make a particular Payload irrelevant from an offensive perspective. However, such scenarios require significantly different starting conditions and are not the focus of this article.</p>

<hr />

<h2 id="so-how-do-you-actually-create-a-configuration-profile" class="post-h2-center">So How Do You Actually Create a Configuration Profile?</h2>

<p>During the research, one of the first questions that came up was how to actually create such a profile. Officially, Apple has provided tools over the years such as iPhone Configuration Utility and later Apple Configurator, designed to assist in managing and configuring devices. These tools work well, but depend on using Apple’s own products.</p>

<figure>
  <img src="/assets/images/ios-trust/image7.png" alt="Figure 7" />
  <figcaption>Figure 7 - A CA Certificate as a strategic asset</figcaption>
</figure>

<figure>
  <img src="/assets/images/ios-trust/image8.png" alt="Figure 8" />
  <figcaption>Figure 8 - What an attacker can do after MDM Enrollment</figcaption>
</figure>

<p>Today, most organizations create and distribute profiles through various MDM systems. But when I tried to find a simple tool that would allow building and analyzing Configuration Profiles for research purposes, I could not find a solution that fit exactly what I was looking for.</p>

<p>But as noted, in practice a <code class="language-plaintext highlighter-rouge">.mobileconfig</code> file is ultimately just an Apple Property List in XML format. In other words, there is no magic. If you can produce XML (i.e., write text), you can produce a Configuration Profile.</p>

<p>From this understanding, during the research an open-source tool called <code class="language-plaintext highlighter-rouge">ios-profile-builder</code> was built, which allows creating and analyzing Configuration Profiles for learning, research, and security testing purposes.</p>

<p><a href="https://github.com/danieloz147/ios-profile-builder">github.com/danieloz147/ios-profile-builder</a></p>

<hr />

<h2 id="the-many-faces-of-trust-me-bro" class="post-h2-center post-h2-emphasis">The Many Faces of "Trust Me Bro"</h2>

<p>So we have arrived at the really interesting part - seeing the impact live and exploring how far each setting can be taken. Throughout this section I will examine several different types of profiles and their potential impact.</p>

<figure>
  <img src="/assets/images/ios-trust/image9.png" alt="Figure 9" />
  <figcaption>Figure 9 - MDM Dashboard in action</figcaption>
</figure>

<p><strong>But</strong>, before we start talking about Web Clips, digital certificates, VPNs, or MDMs, there is one more fundamental question that needs to be answered. <strong>At the end of the day</strong>, none of the techniques presented below are worth much if the user does not install the profile in the first place.</p>

<p>And this is the central problem that occupies every attacker interested in exploiting Configuration Profiles - how do you get the user to reach the installation screen and click Install?</p>

<p><strong>This</strong> is where one of the most interesting and popular social engineering techniques of recent years enters the picture - ClickFix.</p>

<p><strong>ClickFix: Not making the system perform an action, but making the user perform it.</strong></p>

<p>Behind ClickFix is a fairly simple idea - instead of bypassing security mechanisms, you can get the user to perform a legitimate action that the system was designed to allow.</p>

<p>In various campaigns, users were asked to run PowerShell commands, paste commands into a terminal, install browser extensions, approve system permissions, or perform other actions that under normal circumstances would raise suspicion. What all these cases have in common is that the system is not breached. It simply receives a lawful instruction from a user who was convinced to execute it.</p>

<p>You can look at ClickFix as transferring the execution mechanism from the attacker to the user. Instead of running code remotely, the attacker provides instructions. The user executes them. The operating system completes the rest.</p>

<p>At this point, the connection to the world of Configuration Profiles almost presents itself. If iOS does not trust applications but trusts user decisions, and if a Configuration Profile is an entirely legitimate mechanism of the operating system, then the mission is not to bypass iOS’s security mechanisms. <strong>The mission is simply to get the user to use them.</strong></p>

<figure>
  <img src="/assets/images/ios-trust/image10.png" alt="Figure 10" />
  <figcaption>Figure 10 - What the user sees and what they don't</figcaption>
</figure>

<p>It is important to note that ClickFix is not the only technique, and not necessarily the most common one for distributing Configuration Profiles. In fact, in most scenarios examined later, we are not dealing with ClickFix in its classic sense.</p>

<p>The real value of ClickFix in this context is not in the technique itself but in the way of thinking it represents. Instead of focusing on how to bypass a security mechanism, it focuses attention on how to get the user to perform a legitimate action that the system already knows how to execute.</p>

<p>In this sense, ClickFix served primarily as conceptual inspiration for the research. It provides a convenient way to look at the same fundamental problem - how to get the user to voluntarily expand the device’s circle of trust.</p>

<p>For the purposes of this research, a basic ClickFix template was built demonstrating the referral process for installing a Configuration Profile. In a real scenario, the content, branding, and messaging would be tailored to the target audience and campaign objective.</p>

<p><a href="https://github.com/danieloz147/ios-clickfix-templates">github.com/danieloz147/ios-clickfix-templates</a></p>

<hr />

<h3 id="so-how-does-the-malicious-profile-reach-the-user">So How Does the Malicious Profile Reach the User?</h3>

<p>If ClickFix answers the question “why did the user click Install?”, another question follows almost immediately - how did the profile reach them in the first place?</p>

<p>A Configuration Profile is no different from any other file. Before it can be installed, the user needs to receive it, open it, and start the installation process. This is where the Delivery stage enters the picture.</p>

<p>In the world of phishing and social engineering, Delivery is the channel through which the content reaches the victim. An SMS message, an email, a link on a website, a QR code, or even a legitimate internal organizational system - all are ultimately just means of delivering that content to the user. In the context of Configuration Profiles, the Delivery stage does not determine what the profile will do after installation. It only determines how the user will be exposed to it in the first place.</p>

<p>The exploits presented later are not dependent on a specific distribution channel. The same profile can reach the user through a wide range of mechanisms, and the examples that follow are examples only. In practice, almost any channel capable of getting a user to open a link or download a file can potentially also be used to distribute a profile.</p>

<hr />

<h2 id="trust-me-bro-its-our-app" class="post-h2-center">Trust Me Bro, It's Our App</h2>

<h3 id="what-does-it-do">What Does It Do?</h3>

<p>A Web Clip is a Payload that adds an icon to the home screen as a shortcut to a website - nothing more. Despite the appearance, it is not an application, no code is installed on the device, and no additional system permissions are granted.</p>

<p>The main advantage of a Web Clip is that it is configured to open in full-screen mode - the site loads without the familiar browser UI elements the user is used to seeing. The resulting experience closely resembles a dedicated app, especially when the site itself was designed accordingly.</p>

<h3 id="abuse-case">Abuse Case</h3>

<p>At first glance, a Web Clip does not appear particularly interesting from an offensive standpoint. After all, you can send a user a link via SMS, email, or WhatsApp just as easily. The key difference is that a Web Clip gives the site a permanent presence on the device’s home screen. With the right choice of name, icon, and UI, it can be made to look like a legitimate organizational app installed by the IT department.</p>

<p>From iOS’s perspective, of course, no application was installed at all. From the user’s perspective, however, the difference is not always obvious.</p>

<p>And this is where it starts to get interesting.</p>

<p>Modern websites now have access to a wide range of Web APIs that allow them to perform actions once reserved almost exclusively for dedicated applications. Depending on user approval and browser-supported capabilities, a website may gain access to:</p>

<ul>
  <li><strong>Geolocation API</strong> - receive geographic location</li>
  <li><strong>Camera API</strong> - access to the camera</li>
  <li><strong>Microphone API</strong> - access to the microphone</li>
  <li><strong>Notifications API</strong> - display Push notifications (in supported cases)</li>
  <li><strong>Clipboard API</strong> - read and write to the clipboard</li>
  <li><strong>File Upload APIs</strong> - access to files the user chooses to share</li>
  <li><strong>Device Orientation / Motion APIs</strong> - data from motion and orientation sensors</li>
  <li><strong>WebRTC</strong> - real-time audio, video, and Data Channels communication</li>
</ul>

<p>It is important to emphasize that each of these capabilities is subject to Safari’s permission and restriction mechanisms. A Web Clip does not bypass the operating system’s security model and does not provide unrestricted device access. However, as long as the Web Clip remains open and is not fully closed, the site can continue using approved permissions without presenting additional requests - a significant advantage for an attacker.</p>

<div class="post-video">
  <iframe src="https://www.youtube.com/embed/kcYw7sxnPiU?rel=0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>

<hr />

<h3 id="the-delivery-method-used-here-arbitrarily-chosen">The Delivery Method Used Here (Arbitrarily Chosen)</h3>

<p>An SMS message containing the relevant content, crafted to appear as part of an existing conversation thread with a known sender.</p>

<p><strong>Putting it all together - full attack demo:</strong></p>

<div class="attack-chain">
  <span class="step">SMS from known source</span>
  <span class="arrow">&#8594;</span>
  <span class="step">ClickFix site</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Profile installation</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Web Clip on home screen</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Permission requests</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Attacker gets all</span>
</div>

<div class="post-video">
  <iframe src="https://www.youtube.com/embed/PgNEYHwKYnU?rel=0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>

<hr />

<p>During the research, I found that the most interesting part is not necessarily the Web Clip itself, but the story built around it. Technically, a Web Clip is just an advanced shortcut to a web page. But when presented as part of a credible campaign - with government-style branding, consistent messaging, and ongoing updates - it becomes a tool of significant power.</p>

<p>This is essentially the principle of “Trust Me, It’s Our App”. The user doesn’t install the Web Clip because they understand what a Web Clip is, but because they believe the story presented to them. Once initial trust is established, the attacker can maintain that trust over time by updating the content, changing the messages, and adapting them to current events.</p>

<p>For the purpose of this research I developed a dedicated C2 server that allows managing and updating Web Clips in a centralized manner. During the work I also found methods that allow making the installation process completely seamless to the user through a specific approach to configuration profile installation - but since that knowledge could be misused excessively, I chose not to detail it in this article.</p>

<p>The main conclusion is that the risk does not stem only from a specific technical capability, but from the combination of ongoing management capabilities with a credible story that succeeds in making the victim see the Web Clip as a fully legitimate application.</p>

<h3 id="technical-breakdown-of-the-demo">Technical Breakdown of the Demo</h3>

<p>The video presents <strong>WebClip C2</strong> - a Command &amp; Control platform developed for research purposes, demonstrating how Web Clips can be managed centrally, similar to managing Agents in classic C2 systems.</p>

<p>On the right is an iPhone with a Web Clip installed impersonating a legitimate application - in this case “SafeAlert”, simulating an emergency alert and public updates service. On the left is the management interface.</p>

<p><strong>Victim Registration and Identification</strong></p>

<p>Upon opening the Web Clip, the browser communicates with the C2 server and registers as a new device. From that moment it is visible in the control panel with technical details and activity tracking. The system collects:</p>

<ul>
  <li>Unique installation identifier</li>
  <li>OS and browser details</li>
  <li>Granted permissions</li>
  <li>Connection times and activity log</li>
  <li>Fingerprinting data for consistent victim identification</li>
  <li>Geographic location</li>
</ul>

<p>The Intelligence tab shows real-time location data received from the device after user approval - illustrating how a Web Clip that looks like an innocent app can collect high-precision location data.</p>

<p><strong>Local Network Recon</strong></p>

<p>The system performs a scan from within the browser environment to identify:</p>

<ul>
  <li>Default Gateway address</li>
  <li>Active IP addresses</li>
  <li>Accessible ports</li>
  <li>Identifiable internal services</li>
</ul>

<p>This gives the attacker an initial picture of the network environment where the victim is located.</p>

<p><strong>Harvest Module</strong></p>

<p>The Harvest module centralizes various data collection operations, including:</p>

<ul>
  <li>Location collection</li>
  <li>Permission enumeration</li>
  <li>Camera and microphone access testing</li>
  <li>Clipboard interaction</li>
  <li>Push Notifications</li>
  <li>Social Engineering Screens</li>
</ul>

<p>The purpose of these modules is not to exploit a security vulnerability - it is to exploit the user’s trust and the existing browser permission model.</p>

<p><strong>PIN Capture Screen</strong></p>

<p>Near the end of the video, PIN Capture is demonstrated. The operator pushes a dedicated screen to the victim’s device, presenting a UI that simulates a legitimate authentication request. Since the C2 operator controls the page content, messages, design, and social engineering scenarios can be updated in real time.</p>

<p>This is one of the most important points in the research: the real power does not come from one specific technical capability, but from the ability to maintain a credible relationship with the victim over time. The attacker can update the content at any moment, adapt it to current events, and sustain the illusion of a legitimate, actively maintained application.</p>

<p>The full source code is available publicly:</p>

<p><a href="https://github.com/danieloz147/webclip-c2">github.com/danieloz147/webclip-c2</a></p>

<blockquote>
  <p><strong>Side note:</strong> Normally, when a user closes a Web Clip or moves it to the background, its JavaScript environment is suspended and access to permissions stops with it. During the research I found several mechanisms that allow extending the session life and preserving some logic even when the Web Clip is not in the foreground. One method I experimented with used an active media player streaming an imperceptible audio signal, causing the OS to continue treating the app as active. I also developed a second mechanism based on Notifications, not shown in the video. The inspiration came from the way services like YouTube continue executing logic after the user switches to another app or minimizes the browser window.</p>
</blockquote>

<hr />

<h2 id="trust-me-bro-its-the-company-wi-fi" class="post-h2-center">Trust Me Bro, It's The Company Wi-Fi</h2>

<h3 id="what-does-it-do-1">What Does It Do?</h3>

<p>A Wi-Fi Payload allows pre-configuring a wireless network on the device via a configuration profile. From the user’s perspective, it is a convenient way to automatically connect to a corporate network without entering a password or performing manual setup.</p>

<p>In many organizations this is a completely legitimate process - the IT department sends employees a configuration profile, the user approves the installation, and the device connects automatically to the corporate network. From the operating system’s perspective, this is a normal and familiar management mechanism. From an attacker’s perspective, it is a mechanism built almost entirely on trust that can be exploited.</p>

<h3 id="abuse-case-1">Abuse Case</h3>

<p>Most users have no idea what a Wi-Fi profile actually contains. When presented with a message such as “Install the new corporate network profile” or “Connect to the company emergency network,” most will assume it is a routine action coming from the IT department.</p>

<p>The average user sees a familiar network name, a corporate logo, and instructions that look legitimate. They do not examine the configuration details and do not check what additional settings were included in the profile. And this is where it gets interesting.</p>

<p>For most people, Wi-Fi is infrastructure - something that should “just work.” Precisely because of this, requests related to network connectivity are perceived as less suspicious than installing an application, entering a password, or opening an unknown file. The attacker exploits the built-in trust users have in network connection processes.</p>

<h3 id="the-delivery-method-used-here-arbitrarily-chosen-1">The Delivery Method Used Here (Arbitrarily Chosen)</h3>

<p>For this demonstration, a relatively simple scenario was chosen: the attacker distributes a flyer that appears to have been sent or shared by the company. The flyer contains a QR Code pointing to the installation site.</p>

<p>The user scans the code, goes through an installation process that looks completely legitimate, and approves the configuration profile installation. After the process completes, the device automatically connects to the Wi-Fi network configured by the attacker.</p>

<p>From the moment of connection, the user can be presented with a Captive Portal or a “user authentication” screen impersonating a legitimate corporate authentication mechanism. From the user’s perspective, this is a familiar process of connecting to the corporate network. From the attacker’s perspective, it is an opportunity to harvest credentials or other sensitive information.</p>

<p>Unlike simply providing an SSID and password, a Wi-Fi profile allows the attacker to pre-configure all connection details, precisely choose which networks the device will join, and create an onboarding experience that looks like an official, managed IT process.</p>

<p><strong>Putting it all together - full attack demo:</strong></p>

<div class="attack-chain">
  <span class="step">QR Code</span>
  <span class="arrow">&#8594;</span>
  <span class="step">ClickFix site</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Profile installation</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Device joins attacker Wi-Fi</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Captive Portal appears</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Credential harvesting</span>
</div>

<div class="post-video">
  <iframe src="https://www.youtube.com/embed/5LDbPISBdT0?rel=0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>

<p>Similar to the Web Clip scenario, the power of this attack does not come from a particularly complex technical mechanism, but from the trust the user places in the process. The user connects to the network not because they checked the configuration details, but because they believe it is the legitimate corporate network. It is precisely at this point that a legitimate management mechanism becomes an effective tool in a social engineering-based attack chain.</p>

<h3 id="technical-breakdown-of-the-demo-1">Technical Breakdown of the Demo</h3>

<p>The video presents a full scenario in which a user is asked to connect to the wireless network of a fictitious organization called Aurora Energy Group. The process begins with a dedicated landing page presenting the connection process as a routine IT procedure, providing the user with clear instructions for installing the configuration profile. After approving the profile on the iPhone, the system automatically configures a new Wi-Fi network named Aurora-Employee, and the device connects to it without requiring any password entry or additional user action.</p>

<p>Immediately after connecting, a Captive Portal appears - designed as a seamless continuation of the branding and experience presented in the previous steps. The portal displays a corporate authentication screen and asks the user to enter their credentials to “complete the connection.” From the user’s perspective, this is a unified and continuous process of connecting to the corporate network. From the attacker’s perspective, it is a chain of entirely legitimate components leading to credential harvesting.</p>

<p>The demo illustrates how standard Wi-Fi and Captive Portal mechanisms can be leveraged to create a credible and convincing user experience, without relying on security vulnerabilities or bypassing operating system protections.</p>

<hr />

<h2 id="trust-me-bro-route-everything-through-me" class="post-h2-center">Trust Me Bro, Route Everything Through Me</h2>

<h3 id="what-does-it-do-2">What Does It Do?</h3>

<p>A VPN Payload allows configuring a VPN connection on the device via a configuration profile. After installation, the device can route its network traffic through a pre-configured VPN server, without any manual setup required from the user.</p>

<p>Organizations make extensive use of VPN as an integral part of their network access infrastructure, particularly for remote employees or mobile devices.</p>

<p>It is worth noting that unlike Proxy-based configurations, a VPN solution generally offers broader coverage of network traffic. Since routing occurs at the operating system level, most applications cannot easily bypass it - resulting in more consistent control over the device’s communication paths.</p>

<p>That said, there is also a significant difference in terms of user visibility (OPSEC). Modern operating systems typically display a clear indicator when a VPN connection is active - either through a dedicated icon or a status bar indication. In practice, many users tend to ignore this indicator or do not understand its significance, but it is still a visual cue that does not always exist with other mechanisms.</p>

<p>Although similar results can be achieved using a Global HTTP Proxy Payload, I chose to focus on VPN since it does not depend on Supervised Device management requirements. For this research scenario, VPN provides a simpler, more practical, and more realistic attack chain.</p>

<p>It is also important to note that installing a VPN alone does not automatically grant the ability to inspect or decrypt HTTPS traffic. To do so, a trusted Root CA certificate must also be deployed to the target devices. Technically, the certificate can be included as part of the same configuration profile, but modern operating systems have additional protection mechanisms that require the user to explicitly approve trust in the certificate.</p>

<p>This creates an additional friction point in the deployment process. In my experience, users do not make such decisions based on a technical understanding of what a Root CA means, but based on their level of trust in the party requesting the installation. Therefore, as with other management mechanisms discussed in this article, the most significant factor here is not purely technical - it is human. The ability to convince the user that this is a legitimate component required for security improvement, troubleshooting, or access to organizational services.</p>

<h3 id="abuse-case-2">Abuse Case</h3>

<p>Most users do not understand the practical implications of installing a VPN profile or the extent of its effect on device communications. When presented with a message like “Security update required for access to company services” or “Install Secure Access Gateway to improve browsing security,” they will typically treat it as just another technical step in the corporate onboarding process.</p>

<p>From the user’s perspective, nothing changes materially. Applications continue to work, websites continue to load, and daily work continues as normal.</p>

<p>From the attacker’s perspective, however, the device may begin routing a significant portion - or all - of its network traffic through infrastructure under their control.</p>

<h3 id="the-delivery-method-used-here-arbitrarily-chosen-2">The Delivery Method Used Here (Arbitrarily Chosen)</h3>

<p>For this demonstration, a scenario was chosen in which the attacker sends an email impersonating an official communication from the IT department. The message presents a security initiative or infrastructure upgrade and includes a QR Code pointing to a dedicated installation page.</p>

<p>From the user’s perspective, this is just another routine organizational process designed to improve browsing security or access to internal services.</p>

<p>Upon completing the installation, the new network settings are applied to the device, and the user continues using it completely normally - without being required to take any further action. For the user, this is a standard onboarding process. For the attacker, it is a new control point obtained through social engineering alone.</p>

<p><strong>Putting it all together - full attack demo:</strong></p>

<div class="attack-chain">
  <span class="step">Email</span>
  <span class="arrow">&#8594;</span>
  <span class="step">QR Code</span>
  <span class="arrow">&#8594;</span>
  <span class="step">ClickFix site</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Profile installation</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Traffic routed through attacker infrastructure</span>
</div>

<div class="post-video">
  <iframe src="https://www.youtube.com/embed/AZxsZV2G2Jk?rel=0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>

<h3 id="technical-breakdown-of-the-demo-2">Technical Breakdown of the Demo</h3>

<p>The video presents a scenario in which a user receives a message impersonating an official communication from the organization’s IT department. The message describes recently discovered connectivity issues and invites the user to install a new network configuration update by scanning a QR Code attached to the email.</p>

<p>After scanning the code, the user is directed to a dedicated installation site and installs a configuration profile containing VPN settings. The installation process is presented as part of a legitimate infrastructure upgrade designed to improve connection stability and access to organizational services.</p>

<p>Upon completion of the installation, the device establishes a VPN connection and begins routing network traffic through the infrastructure configured in the profile. On the attacker side, the new device appears in real time in the management interface - including its assigned IP address, connection status, and additional activity information.</p>

<p>The video then demonstrates several modules for analyzing traffic passing through the infrastructure, including service and site classification, domain information observed in traffic, usage statistics, and additional data collected as part of monitoring activity.</p>

<p>One of the interesting aspects of this scenario is that it can serve as an initial stage in a broader attack chain. Instead of relying on an exploit or code execution, the attacker achieves an initial foothold through legitimate management mechanisms and the trust the user places in them.</p>

<hr />

<h2 id="trust-me-bro-im-it" class="post-h2-center">Trust Me Bro, I'm IT</h2>

<h3 id="what-does-it-do-3">What Does It Do?</h3>

<p>An MDM Enrollment Payload allows registering a device with a Mobile Device Management system. After enrollment, the management server can manage various aspects of the managed device according to the permissions granted, the profiles installed, and the organizational policy configured.</p>

<p>MDM is one of Apple’s central management mechanisms, used by organizations worldwide to manage endpoints, distribute configurations, install profiles, enforce security policies, deploy digital certificates, configure VPN, Wi-Fi, corporate email, and more.</p>

<p>Unlike the other Payloads covered in this article, MDM is not interesting because of one specific action it can perform. It is interesting because it changes the relationship between the device and the server. Instead of installing a single component, the user grants the server the status of an administrative authority. From that point on, many of the actions presented in earlier sections can be carried out as part of the system’s legitimate management mechanism. In a sense, this is the Final Boss of the Trust Me Bro series.</p>

<h3 id="abuse-case-3">Abuse Case</h3>

<p>Most users do not fully understand what enrolling a device in an MDM system means. When presented with a message like “Your device needs to be enrolled in company services” or “Install a management component to access organizational resources,” most will treat it as a natural part of the IT process.</p>

<p>From the user’s side:</p>

<ul>
  <li>They see official iOS system screens</li>
  <li>They see the term Remote Management</li>
  <li>They see the organization’s name</li>
  <li>They see the system itself requesting approval</li>
</ul>

<p>Precisely because all of these elements come from the operating system, they reinforce the sense of legitimacy and reduce the user’s level of suspicion. In practice, the user does not approve the enrollment because they understand what an MDM system is or what permissions they are granting. They approve it because they trust that the party in front of them is the IT department.</p>

<p>And that is exactly the point that makes this Payload unique.</p>

<p>If each of the previous sections dealt with exploiting a single management mechanism - Web Clip, Wi-Fi, VPN, or Certificates - MDM sits above all of them. It is not just another Payload; it is a mechanism capable of deploying and managing many of those same components as part of a legitimate management process.</p>

<h3 id="the-delivery-method-used-here-arbitrarily-chosen-3">The Delivery Method Used Here (Arbitrarily Chosen)</h3>

<p>For this demonstration, a scenario was chosen in which the user connects their iPhone to a computer controlled by someone impersonating IT. The user is asked to approve the “Trust This Computer” prompt as part of a support, diagnostic, or device enrollment process.</p>

<p>After approving the Trust, a Pairing Record is created between the computer and the device. From this point, legitimate tools such as <code class="language-plaintext highlighter-rouge">pymobiledevice3</code> can be used to communicate with iOS management services via Lockdown and usbmux interfaces. In the demonstration scenario, the tool is used to install a configuration profile that initiates MDM Enrollment - using Apple-supported mechanisms and without exploiting any operating system vulnerability.</p>

<p>The key research point is that the user does not perceive the Trust action as granting significant administrative access. From their perspective, it is a momentary technical approval required so that “the computer can recognize the iPhone.” In practice, this is an important trust transfer - once the device is paired, the computer gains the ability to perform certain management interactions with the device as long as iOS restrictions, lock state, and user permissions allow it.</p>

<blockquote>
  <p><strong>Important note:</strong> Contrary to what is sometimes assumed, enrolling a device in an MDM system does not turn the MDM server into a C2 and does not grant unlimited control over the device. Even after enrollment, the MDM server is subject to Apple’s permission model and management capabilities, and cannot perform arbitrary actions or execute code freely.</p>
</blockquote>

<p>That said, precisely because this is a legitimate, high-trust management mechanism, it can serve as an effective starting point for a broader attack chain. After the user completes the enrollment process, additional management components supported by the platform can be deployed, additional organizational policies can be applied, and further management actions can be performed as part of the relationship established between the device and the server. There is the possibility of taking the scenario further - but since this article is already long, that will be discussed in a separate article.</p>

<p><strong>Putting it all together - full attack demo:</strong></p>

<div class="attack-chain">
  <span class="step">"Let me diagnose your phone"</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Trust This Computer</span>
  <span class="arrow">&#8594;</span>
  <span class="step">Managed Profile Deployment</span>
  <span class="arrow">&#8594;</span>
  <span class="step">MDM Enrollment</span>
</div>

<div class="post-video">
  <iframe src="https://www.youtube.com/embed/UrZ2bbmvb7g?rel=0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>

<h3 id="technical-breakdown-of-the-demo-3">Technical Breakdown of the Demo</h3>

<p>For the demonstration, a personal MDM server was built specifically for this research. The reason for choosing a personal MDM server was to illustrate that the enrollment and trust mechanism presented by iOS is identical to the one used by organizations in production environments.</p>

<p>At the end of the demo, the device has successfully enrolled in the MDM server and appears in the management console as a managed device. This is where the scenario presented in this article ends. Unlike the previous sections, where the focus was on the capabilities of a single Payload, the emphasis here is on establishing an ongoing relationship between the device and the management server - a relationship from which further management actions can continue to be performed within the scope of Apple’s legitimate capabilities.</p>

<p>To reproduce the research environment, a full infrastructure was developed based on <code class="language-plaintext highlighter-rouge">pymobiledevice3</code> and an MDM server, including all setup components, scripts, and instructions required to build an identical research environment. The full source code, installation instructions, and project documentation are available here:</p>

<p><a href="https://github.com/danieloz147/nanohub-and-mdm">github.com/danieloz147/nanohub-and-mdm</a></p>

<blockquote>
  <p><strong>Important tip:</strong> To enhance the sense of legitimacy, a public MDM service offering a free trial can be used. As a result, the enrollment process occurs against a real MDM server, and the profile presented to the user is signed and identified by iOS exactly as it would appear in a legitimate corporate enrollment. From the user’s perspective, there is no indication that the environment belongs to an attacker.</p>

  <p>Such services are available at:</p>
  <ul>
    <li><strong>SimpleMDM</strong> - offers a free trial after registration - <a href="https://simplemdm.com">simpleMDM.com</a></li>
    <li><strong>Jamf Pro</strong> - offers a free trial after registration - <a href="https://jamf.com/products/jamf-now">jamf.com/products/jamf-now</a></li>
    <li><strong>Mosyle</strong> - offers a free plan for small organizations (up to a limited number of Apple devices), as well as extended trial periods for advanced plans - <a href="https://mosyle.com">mosyle.com</a></li>
  </ul>
</blockquote>

<hr />

<h2 id="what-comes-next" class="post-h2-center post-h2-emphasis">What Comes Next</h2>

<p>Although this article covered a wide range of Payloads supported by Apple Configuration Profiles, it is far from exhausting all the possibilities the platform offers. Its purpose was to present the key friction points between legitimate management mechanisms and the trust users place in them - not to serve as a complete map of all iOS capabilities.</p>

<p>Throughout the work, I focused primarily on scenarios where the user is the central and weakest link in the chain.</p>

<p>One topic not covered in this article - but which will be the focus of the next research - is examining the boundary between Apple’s legitimate management mechanisms and the ability to turn them into a persistent management platform. The next article will examine what can be done after trust has already been granted. It will present scenarios including managed application deployment, establishing a persistent communication channel (C2) based on legitimate platform mechanisms, and combining additional management capabilities to understand how far the boundaries of MDM can be stretched without exceeding Apple’s security model. In other words, if this article was about creating the relationship - the next one will be about how far it can be taken.</p>

<p>Finally, I believe the most interesting part is not technical at all. Ultimately, every scenario presented in this article rests on the same simple premise: users do not place their trust in technology - they place their trust in the story told to them. Understanding the relationship between Apple’s management mechanisms and users’ perception of trust is, in my view, one of the most interesting research directions for offensive security.</p>

<hr />

<h2 id="references" class="post-h2-center post-h2-emphasis">References</h2>

<ul>
  <li><a href="https://support.apple.com/guide/security/sec59b0b31ff/web">Apple Platform Security - Configuration Profiles</a></li>
  <li><a href="https://support.apple.com/guide/security/secb3000f149/web">Apple Platform Security - Mobile Device Management</a></li>
  <li><a href="https://support.apple.com/guide/deployment/dep5370d089/web">Apple Deployment Reference - Configuration Profiles</a></li>
  <li><a href="https://support.apple.com/guide/apple-configurator-mac/pmd85719196/mac">Apple Configurator - Profile Reference</a></li>
  <li><a href="https://caniuse.com">Can I Use - Web API browser support</a></li>
</ul>]]></content><author><name>Daniel Oz</name></author><category term="mobile" /><category term="iOS" /><category term="red-team" /><category term="Phishing" /><category term="MDM" /><category term="configuration-profiles" /><summary type="html"><![CDATA[An in-depth look at how Apple configuration profiles and the iOS trust model can be abused for realistic phishing and red-team scenarios without exploiting a software vulnerability.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danieloz147.github.io/assets/images/ios-trust/post-exploiting-ios-trust-model.png" /><media:content medium="image" url="https://danieloz147.github.io/assets/images/ios-trust/post-exploiting-ios-trust-model.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>