<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>vulnerability on SecMate Blog</title><link>https://blog.secmate.dev/tags/vulnerability/</link><description>What we find. What we share.</description><generator>Hugo</generator><language>en-us</language><managingEditor>noreply@blog.secmate.dev//blog.secmate.dev/ (SecMate Team)</managingEditor><lastBuildDate>Tue, 25 Aug 2026 06:06:13 +0000</lastBuildDate><atom:link href="https://blog.secmate.dev/tags/vulnerability/index.xml" rel="self" type="application/rss+xml"/><item><title>Corrupting the Secure World: Three Memory-Safety Flaws in OP-TEE</title><link>https://blog.secmate.dev/posts/optee-vulnerabilities-disclosure/</link><pubDate>Wed, 22 Jul 2026 05:00:00 +0000</pubDate><dc:creator>Maxime Rossi Bellom</dc:creator><dc:creator>Ramtine Tofighi Shirazi</dc:creator><category>Security</category><category>Vulnerability Research</category><category>Embedded</category><guid>https://blog.secmate.dev/posts/optee-vulnerabilities-disclosure/</guid><description>Technical disclosure of three OP-TEE memory-safety vulnerabilities: a SHA-3 accelerated-finalize heap overflow (CVE-2026-40257), an RSASSA PKCS#1 v1.5 underflow (CVE-2026-33662), and an SE050 RSA NOPAD heap underflow. We cover the Arm Trusted Firmware architecture, attack chains across the S-EL0/S-EL1 boundary, OP-TEE mitigations, and the upstream response.</description><content:encoded><![CDATA[<h1 id="corrupting-the-secure-world-three-memory-safety-flaws-in-op-tee">Corrupting the Secure World: Three Memory-Safety Flaws in OP-TEE</h1>
<p>SecMate&rsquo;s automated analysis found three security issues in <a href="https://github.com/OP-TEE/optee_os" rel="noopener noreferrer" target="_blank">OP-TEE</a> <a href="#ref1">[1]</a>, the open-source Trusted OS for Arm TrustZone that is commonly deployed with Arm Trusted Firmware. All three corrupt memory in the privileged OP-TEE core. We reported them against commit <code>f2a7ad0638aeff5243593b33cc56ad064cae7615</code>.</p>
<p>Two received CVEs. The affected SHA-3 path shipped in 13 public OP-TEE releases, the PKCS#1 v1.5 path in 25, and the SE050 RSA NOPAD path in at least 18. The oldest issue had been present for more than six years. SecMate found all three automatically using locally deployable gpt-oss-20b and gpt-oss-120b models.</p>
<p>OP-TEE treated the third finding as a bug because our report did not demonstrate the initial compromise needed to establish a complete compromised-TA attack chain.</p>
<p>Core memory corruption matters because OP-TEE is responsible for keeping secrets isolated from Linux or Android.</p>
<h2 id="why-op-tee-matters">Why OP-TEE matters</h2>
<p>OP-TEE stands for <strong>Open Portable Trusted Execution Environment</strong>. On Arm TrustZone systems, it runs security-sensitive code and data separately from the normal operating system, usually Linux or Android <a href="#ref1">[1]</a>.</p>
<p>That normal operating system is called the <strong>Rich Execution Environment (REE)</strong>. OP-TEE runs on the protected side of the TrustZone boundary. The separation is meant to keep sensitive data, code, and execution isolated even if software in the normal world is compromised. The GlobalPlatform TEE architecture defines integrity and data confidentiality as core security properties. It also defines mechanisms for Trusted Application code confidentiality and isolation from the REE <a href="#ref2">[2]</a>. The protected properties include:</p>
<ul>
<li>confidentiality of sensitive data and keys</li>
<li>integrity of code and data during execution</li>
<li>confidentiality of Trusted Application code when provided by the implementation</li>
<li>isolation from the normal operating system</li>
</ul>
<p>OP-TEE is not limited to research platforms. TrustedFirmware.org describes it as widely used and deployed, and its official platform list includes maintained targets from STMicroelectronics, NXP, Texas Instruments, Renesas, Rockchip, and AMD/Xilinx <a href="#ref3">[3]</a> <a href="#ref4">[4]</a>. Other vendors use different secure environments, including Qualcomm TEE, Samsung TEEGRIS, and Apple&rsquo;s Secure Enclave with <code>sepOS</code> <a href="#ref5">[5]</a> <a href="#ref6">[6]</a> <a href="#ref7">[7]</a>. A device with a TEE therefore does not necessarily run OP-TEE.</p>
<h2 id="what-arm-trusted-firmware-and-op-tee-actually-are">What Arm Trusted Firmware and OP-TEE actually are</h2>
<p>Arm application processors implement two security states, the <strong>Normal World</strong> and the <strong>Secure World</strong>, selected by the <code>NS</code> bit and enforced in hardware by TrustZone. The idea is to keep a small, high-assurance environment isolated from a large, complex Rich OS such as Linux. Inside each world, the processor still has privilege levels, expressed as exception levels.</p>
<p><a href="https://www.trustedfirmware.org/projects/tf-a/" rel="noopener noreferrer" target="_blank">Arm Trusted Firmware (TF-A)</a> <a href="#ref9">[9]</a> is a reference implementation of the firmware that commonly runs at the most privileged level, <strong>EL3</strong>. Its <code>BL31</code> runtime acts as the <strong>Secure Monitor</strong> in this deployment model. It handles <code>SMC</code> (Secure Monitor Call) instructions, PSCI power management, world switches, and dispatch to a Trusted OS.</p>
<p><strong>OP-TEE</strong> is the Trusted OS in the deployment shown below. It commonly runs as the <code>BL32</code> secure payload at <strong>S-EL1</strong>, with Trusted Applications (TAs) at <strong>S-EL0</strong>. OP-TEE and TF-A are sibling <a href="https://www.trustedfirmware.org/" rel="noopener noreferrer" target="_blank">TrustedFirmware.org</a> projects. When deployed together, TF-A provides the monitor and dispatches calls to OP-TEE. From the Normal World, an application uses the GlobalPlatform TEE Client API. The call crosses into EL3 via <code>SMC</code>, and the monitor hands it to OP-TEE.</p>
<figure class="optee-architecture" role="img" aria-label="Arm TrustZone architecture showing the Normal World, Secure World, TF-A at EL3, OP-TEE core at S-EL1, and Trusted Applications at S-EL0"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 680 540" class="optee-diagram" aria-hidden="true" focusable="false">
  <defs>
    <style>
      .opt-canvas { fill: var(--color-bg, #111827); }
      .opt-txt { fill: var(--color-text, #e5e7eb); font-family: system-ui, sans-serif; }
      .opt-muted { fill: var(--color-text-secondary, #9ca3af); font-family: system-ui, sans-serif; }
      .opt-acc { fill: var(--color-accent, #84cc16); font-family: system-ui, sans-serif; }
      .opt-red { fill: #ef4444; font-family: system-ui, sans-serif; }
      .opt-box { fill: var(--color-bg-card, #1f2937); stroke: var(--color-accent, #84cc16); stroke-width: 2; }
      .opt-monitor { fill: var(--color-bg-card, #1f2937); fill: color-mix(in srgb, var(--color-accent, #84cc16) 12%, var(--color-bg-card, #1f2937)); stroke: var(--color-accent, #84cc16); stroke-width: 2; }
      .opt-vuln { fill: var(--color-bg-card, #1f2937); fill: color-mix(in srgb, #ef4444 12%, var(--color-bg-card, #1f2937)); stroke: #ef4444; stroke-width: 2; }
      .opt-entry { fill: var(--color-bg-card, #1f2937); stroke: #f59e0b; stroke-width: 2; }
      .opt-dashed { fill: none; stroke: var(--color-text-secondary, #9ca3af); stroke-width: 1.5; stroke-dasharray: 5,3; }
      .opt-hw { fill: none; stroke: var(--color-accent, #84cc16); stroke-width: 1.5; stroke-dasharray: 6,3; }
      .opt-line { stroke: var(--color-accent, #84cc16); stroke-width: 1.5; }
      .opt-line-red { stroke: #ef4444; stroke-width: 2; }
      .opt-tz { stroke: var(--color-text-secondary, #9ca3af); stroke-width: 1.5; stroke-dasharray: 6,4; }
      .opt-arrow { fill: var(--color-accent, #84cc16); }
      .opt-arrow-red { fill: #ef4444; }
      .opt-red-ring { stroke: #ef4444; }
    </style>
    <marker id="opt-arrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" class="opt-arrow"/>
    </marker>
    <marker id="opt-arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" class="opt-arrow-red"/>
    </marker>
  </defs>

  <rect width="680" height="540" rx="6" class="opt-canvas"/>

  <!-- World labels -->
  <text x="182" y="20" text-anchor="middle" font-size="13" font-weight="700" class="opt-acc" letter-spacing="1">NORMAL WORLD (NS=1)</text>
  <text x="497" y="20" text-anchor="middle" font-size="13" font-weight="700" class="opt-red" letter-spacing="1">SECURE WORLD (NS=0)</text>

  <!-- TrustZone boundary -->
  <line x1="340" y1="28" x2="340" y2="262" class="opt-tz"/>

  <!-- EL0 -->
  <text x="14" y="62" font-size="11" font-weight="600" class="opt-muted">EL0</text>
  <rect x="40" y="40" width="285" height="56" rx="4" class="opt-box"/>
  <text x="182" y="62" text-anchor="middle" font-size="14" font-weight="500" class="opt-txt">Client Applications</text>
  <text x="182" y="80" text-anchor="middle" font-size="10" class="opt-muted">GlobalPlatform TEE Client API (libteec)</text>

  <rect x="355" y="40" width="285" height="56" rx="4" class="opt-entry"/>
  <text x="497" y="62" text-anchor="middle" font-size="14" font-weight="500" class="opt-txt">Trusted Applications (TAs)</text>
  <text x="497" y="80" text-anchor="middle" font-size="10" class="opt-muted">User-mode applications signed for the target platform</text>
  <text x="674" y="62" font-size="11" font-weight="600" class="opt-muted" text-anchor="end">S-EL0</text>

  <!-- EL1 -->
  <text x="14" y="142" font-size="11" font-weight="600" class="opt-muted">EL1</text>
  <rect x="40" y="116" width="285" height="62" rx="4" class="opt-box"/>
  <text x="182" y="140" text-anchor="middle" font-size="14" font-weight="500" class="opt-txt">Rich OS (Linux)</text>
  <text x="182" y="158" text-anchor="middle" font-size="10" class="opt-muted">OP-TEE driver, tee-supplicant</text>

  <rect x="355" y="116" width="285" height="62" rx="4" class="opt-vuln"/>
  <text x="497" y="138" text-anchor="middle" font-size="14" font-weight="600" class="opt-txt">OP-TEE OS (Trusted OS)</text>
  <text x="497" y="156" text-anchor="middle" font-size="10" class="opt-muted">core, GP syscalls, libtomcrypt, crypto drivers</text>
  <text x="497" y="170" text-anchor="middle" font-size="10" class="opt-muted">SE050 / CAAM / accelerated paths, S-EL1 heap</text>
  <circle cx="620" cy="138" r="9" fill="none" stroke-width="1.5" class="opt-red-ring"/>
  <text x="620" y="142" text-anchor="middle" font-size="12" font-weight="700" class="opt-red">!</text>
  <text x="674" y="142" font-size="11" font-weight="600" class="opt-muted" text-anchor="end">S-EL1</text>

  <!-- Secure-world attack arrow S-EL0 -> S-EL1 -->
  <line x1="497" y1="96" x2="497" y2="114" class="opt-line-red" marker-end="url(#opt-arrow-red)"/>
  <text x="505" y="110" font-size="9" class="opt-red">GP TEE syscalls</text>

  <!-- EL2 -->
  <text x="14" y="222" font-size="11" font-weight="600" class="opt-muted">EL2</text>
  <rect x="40" y="198" width="285" height="44" rx="4" class="opt-dashed"/>
  <text x="182" y="224" text-anchor="middle" font-size="13" class="opt-muted">Hypervisor (optional)</text>

  <rect x="355" y="198" width="285" height="44" rx="4" class="opt-dashed"/>
  <text x="497" y="224" text-anchor="middle" font-size="13" class="opt-muted">S-EL2 (optional)</text>

  <!-- SMC arrows into monitor -->
  <line x1="182" y1="242" x2="182" y2="277" class="opt-line" marker-end="url(#opt-arrow)"/>
  <line x1="497" y1="242" x2="497" y2="277" class="opt-line" marker-end="url(#opt-arrow)"/>
  <text x="340" y="268" text-anchor="middle" font-size="10" class="opt-muted">SMC / world switch</text>

  <!-- EL3 monitor (spans both worlds) -->
  <text x="14" y="306" font-size="11" font-weight="600" class="opt-muted">EL3</text>
  <rect x="40" y="282" width="600" height="58" rx="4" class="opt-monitor"/>
  <text x="340" y="306" text-anchor="middle" font-size="14" font-weight="600" class="opt-txt">Arm Trusted Firmware (TF-A), Secure Monitor / BL31</text>
  <text x="340" y="324" text-anchor="middle" font-size="10" class="opt-muted">SMC dispatch, TrustZone context switch, OP-TEE SPD, PSCI</text>

  <!-- Hardware -->
  <line x1="40" y1="360" x2="640" y2="360" class="opt-line" stroke-dasharray="10,4"/>
  <rect x="40" y="372" width="600" height="58" rx="4" class="opt-hw"/>
  <text x="340" y="396" text-anchor="middle" font-size="14" font-weight="500" class="opt-txt">Arm Cortex-A SoC, TrustZone (NS bit), MMU, GIC, secure storage</text>
  <text x="340" y="414" text-anchor="middle" font-size="10" class="opt-muted">Boot chain: BL1, BL2, BL31 (TF-A), BL32 (OP-TEE), BL33 (Rich OS)</text>

  <!-- Legend -->
  <rect x="40" y="468" width="14" height="14" rx="2" class="opt-vuln"/>
  <text x="62" y="479" font-size="11" class="opt-muted">OP-TEE core code containing the findings</text>
  <rect x="40" y="490" width="14" height="14" rx="2" class="opt-entry"/>
  <text x="62" y="501" font-size="11" class="opt-muted">Trusted Application caller at S-EL0</text>
  <rect x="400" y="468" width="14" height="14" rx="2" class="opt-monitor"/>
  <text x="422" y="479" font-size="11" class="opt-muted">Arm Trusted Firmware (EL3 monitor)</text>
  <text x="400" y="501" font-size="11" class="opt-muted">Red arrow: syscall path into OP-TEE core</text>
</svg>
</figure>

<p>The boundary that matters here is inside the secure world. Trusted Applications run at S-EL0. The OP-TEE core runs above them at S-EL1 and handles privileged operations such as key storage, cryptographic services, and secure peripherals. A TA can call the core through syscalls, but it must never be able to write core memory.</p>
<p>These three issues break that boundary. The SHA-3 issue starts with an off-by-one. The other two start with unchecked size relationships. In all three, an unsigned subtraction wraps. The resulting size or offset makes <code>memset</code> or <code>memcpy</code> write outside its buffer.</p>
<h2 id="the-three-findings">The three findings</h2>
<table>
<thead>
<tr>
<th>#</th>
<th>Vulnerability</th>
<th>Component</th>
<th>Identifier</th>
<th>OP-TEE severity</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>SHA-3 accelerated-finalize heap overflow</td>
<td><code>core/lib/libtomcrypt/sha3_accel.c</code></td>
<td><a href="https://github.com/OP-TEE/optee_os/security/advisories/GHSA-75x4-j8p9-55qv" rel="noopener noreferrer" target="_blank">CVE-2026-40257</a> <a href="#ref10">[10]</a></td>
<td>Moderate (5.5)</td>
<td>Fixed in 4.11.0</td>
</tr>
<tr>
<td>2</td>
<td>RSASSA PKCS#1 v1.5 encode underflow</td>
<td><code>core/drivers/crypto/crypto_api/acipher/rsassa.c</code></td>
<td><a href="https://github.com/OP-TEE/optee_os/security/advisories/GHSA-4cf8-v5g3-73gr" rel="noopener noreferrer" target="_blank">CVE-2026-33662</a> <a href="#ref11">[11]</a></td>
<td>High (7.5)</td>
<td>Fixed in 4.11 (duplicate of an internal finding)</td>
</tr>
<tr>
<td>3</td>
<td>SE050 RSA NOPAD heap underflow</td>
<td><code>core/drivers/crypto/se050/core/rsa.c</code></td>
<td>None (treated as a bug)</td>
<td>n/a</td>
<td>Still present upstream on July 13, 2026</td>
</tr>
</tbody>
</table>
<p>CVE-2026-40257 requires the Arm v8.2 SHA-3 acceleration path. CVE-2026-33662 affects builds registering RSA acceleration through CAAM, HiSilicon, Versal, or SE050, as listed in the OP-TEE advisory. The third issue requires the NXP SE050 RSA driver. These paths are platform-specific, but they ship on production hardware.</p>
<h2 id="scope-and-impact">Scope and impact</h2>
<p>OP-TEE&rsquo;s published security scope excludes a deliberately malicious TA signed with a trusted TA key because the key holder is already part of the trusted computing base <a href="#ref8">[8]</a>.</p>
<p>An attacker may first compromise a legitimate, signed TA through one or more separate vulnerabilities and then invoke the SE050 bug through that TA. In this chain, the attacker does not need the TA&rsquo;s signing key, and the TA retains its valid signature. Our report demonstrated the resulting controlled write from S-EL0 into S-EL1 core memory, but it did not demonstrate the initial TA compromise. OP-TEE therefore treated the finding as a bug rather than a complete vulnerability report.</p>
<p>Findings 1 and 2 do not have that prerequisite. Valid hash input triggers the first. A signing operation with a sufficiently small RSA modulus reaches the second.</p>
<p>All three are heap writes. Stack protection does not help. ASLR and non-executable data make targeted exploitation harder, but the minimum demonstrated impact for findings 1 and 2 is a secure-world crash. Finding 3 gives the attacker control over both the bytes written and their position relative to the allocation.</p>
<h2 id="finding-1-sha-3-accelerated-finalize-heap-overflow-cve-2026-40257">Finding 1: SHA-3 accelerated finalize heap overflow (CVE-2026-40257)</h2>
<p>OP-TEE has two SHA-3 implementations. The default software implementation in <code>sha3.c</code> accumulates input word by word and is not affected by this issue. The other, <code>sha3_accel.c</code>, is compiled in when a platform enables the Arm v8.2 SHA-3 crypto extensions (<code>CFG_CRYPTO_WITH_CE82=y</code>) and buffers input in blocks. It is the accelerated path that is broken.</p>
<p>The Keccak sponge processes input in blocks of <code>block_size</code> bytes (136 for SHA3-256). <code>sha3_process()</code> is supposed to: finish any partial block, run full blocks through the hardware, then buffer the remainder. The bug is an off-by-one in step two:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cm">/* sha3_accel.c:151 - strict greater-than is the bug */</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">inlen</span> <span class="o">&gt;</span> <span class="n">block_size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">block_count</span> <span class="o">=</span> <span class="n">inlen</span> <span class="o">/</span> <span class="n">block_size</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nf">crypto_accel_sha3_compress</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">in</span><span class="p">,</span> <span class="n">block_count</span><span class="p">,</span> <span class="n">digest_size</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">in</span> <span class="o">+=</span> <span class="n">block_count</span> <span class="o">*</span> <span class="n">block_size</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">inlen</span> <span class="o">-=</span> <span class="n">block_count</span> <span class="o">*</span> <span class="n">block_size</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="cm">/* lines 159-160 - reached when inlen == block_size */</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nf">memcpy</span><span class="p">(</span><span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">sb</span> <span class="o">+</span> <span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">byte_index</span><span class="p">,</span> <span class="n">in</span><span class="p">,</span> <span class="n">inlen</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">byte_index</span> <span class="o">+=</span> <span class="n">inlen</span><span class="p">;</span>            <span class="cm">/* byte_index becomes block_size */</span>
</span></span></code></pre></div><p>When a TA feeds <em>exactly</em> one block (136 bytes) with an empty buffer, <code>inlen &gt; block_size</code> is false, the hardware compression is skipped, and the whole block is copied into <code>sb</code> with <code>byte_index</code> left equal to <code>block_size</code>. The finalize functions assume the invariant <code>byte_index &lt; block_size</code>. It no longer holds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cm">/* sha3_done(), lines 194-195 */</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">buf</span><span class="p">[</span><span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">byte_index</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x06</span><span class="p">;</span>                              <span class="cm">/* sb[136], index -&gt; 137 */</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nf">memset</span><span class="p">(</span><span class="n">buf</span> <span class="o">+</span> <span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">byte_index</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">block_size</span> <span class="o">-</span> <span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">byte_index</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="cm">/* block_size(136) - byte_index(137) underflows to 0xFFFFFFFF */</span>
</span></span></code></pre></div><p><code>block_size - byte_index</code> is <code>136 - 137</code> in unsigned arithmetic, i.e. <code>0xFFFFFFFF</code>. The <code>memset</code> then writes roughly <strong>4 GB of zeroes</strong> starting inside the hash state on the S-EL1 heap. <code>sha3_shake_done()</code> has the identical pattern for SHAKE-128/256.</p>
<p><strong>Attack chain and impact.</strong> The trigger is two ordinary GlobalPlatform calls: <code>TEE_DigestUpdate(op, data, 136)</code> then <code>TEE_DigestDoFinal(...)</code>. No special key material, permissions, or object types are required. The input is valid because hashing a block-sized buffer is normal usage. A perfectly honest TA can therefore crash the secure world without intending to. The write consists of zeroes and spans roughly 4 GB. In practice, it crashes the OP-TEE core and takes down its TAs, secure services, and Normal-World clients. The write offers too little control for a realistic code-execution primitive. The advisory rates it Moderate, CVSS 5.5 (<code>AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H</code>).</p>
<p>We reproduced it with an ASAN harness that <code>#include</code>s the real <code>sha3_accel.c</code>, stubs the hardware compress function, and runs the two-call sequence. ASAN reports a heap-buffer-overflow with the stack pointing straight at <code>sha3_accel.c:195</code>.</p>
<p>Introduced by commit <a href="https://github.com/OP-TEE/optee_os/commit/1478437e65c44163f7c96f8a4c5d1532a9312bc3" rel="noopener noreferrer" target="_blank"><code>1478437e65c4</code></a> in March 2023, this vulnerability shipped in 13 public OP-TEE releases, from 3.21.0 through 4.10.0. It remained in the secure-world core for nearly three years before SecMate&rsquo;s automated analysis identified it, and for more than three years before the fix was merged.</p>
<p><strong>The fix.</strong> Change the comparison so a full block is actually compressed, and add a defensive assertion to the finalize paths <a href="#ref10">[10]</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="ln">1</span><span class="cl"><span class="gd">-	if (inlen &gt; block_size) {
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gd"></span><span class="gi">+	if (inlen &gt;= block_size) {
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gi"></span> 		block_count = inlen / block_size;
</span></span><span class="line"><span class="ln">4</span><span class="cl"> 		crypto_accel_sha3_compress(state, in, block_count, digest_size);
</span></span><span class="line"><span class="ln">5</span><span class="cl">...
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="gi">+	LTC_ARGCHK(md-&gt;sha3.byte_index &lt; block_size);
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="gi"></span> 	buf[md-&gt;sha3.byte_index++] = 0x06;
</span></span><span class="line"><span class="ln">9</span><span class="cl"> 	memset(buf + md-&gt;sha3.byte_index, 0, block_size - md-&gt;sha3.byte_index);
</span></span></code></pre></div><p>With <code>&gt;=</code>, an exact-block input is consumed by the hardware compress, <code>inlen</code> drops to zero, and <code>byte_index</code> stays at zero. The added <code>LTC_ARGCHK</code> returns <code>CRYPT_INVALID_ARG</code> if the invariant is violated again. The advisory identifies OP-TEE 4.11.0 and later as patched. The workaround is <code>CFG_CRYPTO_WITH_CE82=n</code>.</p>
<h2 id="finding-2-rsassa-pkcs1-v15-encode-underflow-cve-2026-33662">Finding 2: RSASSA PKCS#1 v1.5 encode underflow (CVE-2026-33662)</h2>
<p>This one lives in the generic crypto-driver framework (<code>CFG_CRYPTO_DRIVER=y</code>), which platforms with RSA acceleration use to dispatch signature operations: NXP CAAM (<code>plat-imx</code>, <code>plat-ls</code>), Xilinx Versal, HiSilicon, and SE050 builds.</p>
<p>PKCS#1 v1.5 signature encoding pads the DigestInfo structure up to the modulus size with <code>0xFF</code> bytes. <code>emsa_pkcs1_v1_5_encode()</code> computes how many padding bytes it needs by subtracting from the modulus size, with no lower-bound check:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cm">/* rsassa.c:48-50 */</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">ps_size</span>  <span class="o">=</span> <span class="n">ssa_data</span><span class="o">-&gt;</span><span class="n">key</span><span class="p">.</span><span class="n">n_size</span> <span class="o">-</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">ps_size</span> <span class="o">-=</span> <span class="n">ssa_data</span><span class="o">-&gt;</span><span class="n">digest_size</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">ps_size</span> <span class="o">-=</span> <span class="mi">10</span> <span class="o">+</span> <span class="n">hash_oid</span><span class="o">-&gt;</span><span class="n">asn1_length</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nf">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">UINT8_MAX</span><span class="p">,</span> <span class="n">ps_size</span><span class="p">);</span>          <span class="cm">/* line 75 */</span>
</span></span></code></pre></div><p>With a 256-bit modulus (32 bytes) and SHA-256 (32-byte digest, 9-byte OID), the arithmetic is <code>32 - 3 - 32 - (10 + 9)</code>, which underflows <code>size_t</code> to roughly <code>SIZE_MAX</code>. The <code>memset</code> then writes <code>0xFF</code> across the entire S-EL1 heap until OP-TEE faults. RFC 3447 Section 9.2 mandates the missing size check <a href="#ref12">[12]</a>. The same file&rsquo;s <code>noasn1</code> and PSS paths already perform their own size validation. This path did not.</p>
<p><strong>Attack chain and impact.</strong> A TA uses a sufficiently small RSA modulus and calls <code>TEE_AsymmetricSignDigest(TEE_ALG_RSASSA_PKCS1_V1_5_SHA256, ...)</code>. The vulnerable core path is reached through that signing operation. Whether an external attacker can reach it depends on how the product exposes the TA to Normal-World clients. Like finding 1, this is a blunt overwrite. It gives an attacker a reliable TEE-wide denial of service, not controlled execution. The OP-TEE advisory rates the issue High, CVSS 7.5 (<code>AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H</code>).</p>
<p>SecMate identified this vulnerability independently before its public disclosure. OP-TEE had already found the issue internally, so the project handled our report as a duplicate and the CVE process was already underway.</p>
<p>This vulnerability was introduced by commit <a href="https://github.com/OP-TEE/optee_os/commit/f5a70e3efb80be4b9bff2c9c811ddc139058e05a" rel="noopener noreferrer" target="_blank"><code>f5a70e3efb80</code></a> in February 2020. It remained in OP-TEE for more than six years and shipped in 25 public releases before the fix was merged in April 2026. Public Git history places the first affected release at 3.9.0, although the GitHub advisory lists versions from 3.8.0.</p>
<p><strong>The fix.</strong> The maintainers replaced the chained subtraction with checked subtraction macros that bail out on underflow <a href="#ref11">[11]</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="ln">1</span><span class="cl"><span class="gd">-	ps_size = ssa_data-&gt;key.n_size - 3;
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gd">-	ps_size -= ssa_data-&gt;digest_size;
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gd">-	ps_size -= 10 + hash_oid-&gt;asn1_length;
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gd"></span><span class="gi">+	if (SUB_OVERFLOW(ssa_data-&gt;key.n_size, 3, &amp;ps_size) ||
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="gi">+	    SUB_OVERFLOW(ps_size, ssa_data-&gt;digest_size, &amp;ps_size) ||
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="gi">+	    SUB_OVERFLOW(ps_size, 10 + hash_oid-&gt;asn1_length, &amp;ps_size))
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="gi">+		return TEE_ERROR_BAD_PARAMETERS;
</span></span></span></code></pre></div><p>An encoding size that would make one of the subtractions wrap now returns <code>TEE_ERROR_BAD_PARAMETERS</code>. The patch prevents the memory corruption. It does not enforce the separate PKCS#1 v1.5 requirement that the padding string contain at least eight bytes. The advisory identifies OP-TEE 4.11 and later as patched. The workaround is to disable RSA acceleration.</p>
<h2 id="finding-3-se050-rsa-nopad-controlled-heap-underflow">Finding 3: SE050 RSA NOPAD controlled heap underflow</h2>
<p>The third finding gives an attacker more control than the two CVEs, but OP-TEE treated it as a bug. It is in the NXP SE050 secure-element driver (<code>CFG_NXP_SE05X=y</code>), used on i.MX6/7/8 platforms that pair the SoC with an SE050 element over I²C.</p>
<p><code>decrypt_nopad()</code> and <code>encrypt_nopad()</code> left-pad the caller&rsquo;s input to the modulus size by computing a destination offset with, again, an unchecked unsigned subtraction:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cm">/* se050/core/rsa.c:356 (and :418) */</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">rsa_len</span> <span class="o">=</span> <span class="nf">crypto_bignum_num_bytes</span><span class="p">(</span><span class="n">key</span><span class="o">-&gt;</span><span class="n">n</span><span class="p">);</span>     <span class="cm">/* e.g. 256 for a 2048-bit key */</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nf">memcpy</span><span class="p">(</span><span class="n">buf</span> <span class="o">+</span> <span class="n">rsa_len</span> <span class="o">-</span> <span class="n">src_len</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">src_len</span><span class="p">);</span>
</span></span></code></pre></div><p>Neither the generic dispatch layer (<code>crypto_acipher_rsanopad_decrypt</code>, which passes <code>cipher_len</code> straight through) nor the SE050 driver checks that <code>src_len &lt;= rsa_len</code>. If a TA supplies input larger than the modulus, the subtraction computes a destination before the allocation. Forming that pointer is undefined behavior in C. On the affected targets, it produces the following relative out-of-bounds write:</p>
<pre tabindex="0"><code>rsa_len = 256, src_len = 300  -&gt;  256 - 300 = (size_t)-44
memcpy(buf - 44, src, 300)    -&gt;  44 attacker-chosen bytes written BEFORE buf
</code></pre><p><code>buf</code> comes from OP-TEE&rsquo;s BGET-backed mempool. An allocated block&rsquo;s <code>prevfree</code> and <code>bsize</code> metadata sit immediately before its payload, so even a short underflow can overwrite allocator state that <code>mempool_free()</code> later consumes.</p>
<p><strong>Why this one is different.</strong> Findings 1 and 2 overwrite with a fixed byte over an unbounded range, which is useful for a crash but offers little control. This is a <strong>controlled heap-underflow write primitive</strong>: the attacker chooses the underflow distance (<code>src_len - rsa_len</code>) and the bytes copied from <code>src</code>. The primitive can corrupt allocator metadata or an adjacent core object before the affected allocation is freed. We did not build a complete S-EL0-to-S-EL1 exploit, but this gives an attacker far more control than findings 1 and 2, especially on constrained 32-bit targets. We confirmed the out-of-bounds write with an ASAN harness around the real <code>rsa.c</code>.</p>
<p>The vulnerable RSA NOPAD implementation entered the SE050 driver in commit <a href="https://github.com/OP-TEE/optee_os/commit/462192731c921460f35043e04a1aec6074682d6b" rel="noopener noreferrer" target="_blank"><code>462192731c92</code></a> in January 2022. It shipped in at least 18 public releases and was still present in upstream commit <a href="https://github.com/OP-TEE/optee_os/commit/611fbaf3415082fe6ae278e5614e0c9608f5ce32" rel="noopener noreferrer" target="_blank"><code>611fbaf34150</code></a> on July 13, 2026, four and a half years later.</p>
<p><strong>Why it was treated as a bug.</strong> Reaching this code requires control of a TA that calls <code>TEE_AsymmetricDecrypt(TEE_ALG_RSA_NOPAD, ...)</code> with oversized input. That control could come from a deliberately malicious TA or from compromising a legitimate, signed TA through another vulnerability. The former is outside OP-TEE&rsquo;s threat model; the latter does not require the attacker to possess a TA signing key and can form the first stage of an exploit chain. Our report demonstrated the controlled S-EL1 write but not the initial TA compromise, so OP-TEE treated the finding as a bug.</p>
<p>The fix is to validate <code>src_len &lt;= rsa_len</code> and <code>msg_len &lt;= n_size</code> in the generic dispatch layer, then repeat the check in the driver before <code>memcpy</code>. The HiSilicon HPRE driver already validates input sizes independently and is not affected.</p>
<h2 id="three-old-flaws-found-automatically">Three old flaws, found automatically</h2>
<p>These issues entered OP-TEE in 2020, 2022, and 2023. They survived across separate accelerated implementations and hardware drivers until SecMate&rsquo;s automated analysis found them.</p>
<p>Every issue in our public <a href="https://secmate.dev/disclosures?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=optee-vulnerabilities-disclosure&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="optee-vulnerabilities-disclosure" data-post-category="security">disclosure record</a> <a href="#ref13">[13]</a> was found by the same engine. SecMate runs with locally deployable models, so vulnerability research can stay inside the operator&rsquo;s environment instead of sending source code to an external model service.</p>
<h2 id="disclosure-timeline">Disclosure timeline</h2>
<div style="position: relative; padding-left: 1.5rem; margin: 1.5rem 0;">
  <div style="position: absolute; left: 0.35rem; top: 0.5rem; bottom: 0.5rem; width: 2px; background: var(--color-border);"></div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-accent); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">March 5, 2026</div>
    <div style="color: var(--color-text);">SecMate sent the three issues to the TrustedFirmware security team, with advisories, proof-of-concepts, and patch suggestions. We stated our 90-day coordinated-disclosure policy and offered to coordinate additional time if needed. The public SHA-3 advisory records March 13 as its received-report date.</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">March 16, 2026</div>
    <div style="color: var(--color-text);">The OP-TEE security team acknowledged the reports and began analysis.</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">March 20, 2026</div>
    <div style="color: var(--color-text);">The PKCS#1 v1.5 underflow was confirmed as a duplicate of an internal finding. The SHA-3 off-by-one was accepted as a security issue. The SE050 RSA NOPAD underflow was classified as a bug because the report did not include the initial compromise needed to demonstrate a complete compromised-TA attack chain.</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">March 23, 2026</div>
    <div style="color: var(--color-text);">OP-TEE shared <code>CVE-2026-33662</code> and confirmed the requested credit.</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #22c55e; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">April 24, 2026</div>
    <div style="color: var(--color-text);">OP-TEE published the fix and advisory for <code>CVE-2026-33662</code> (GHSA-4cf8-v5g3-73gr), identifying version 4.11 and later as patched.</div>
  </div>
  <div style="position: relative;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #22c55e; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">June 15, 2026</div>
    <div style="color: var(--color-text);">OP-TEE published the fix and advisory for <code>CVE-2026-40257</code> (GHSA-75x4-j8p9-55qv), identifying version 4.11.0 and later as patched and crediting SecMate's automated analyzer.</div>
  </div>
</div>
<h2 id="references">References</h2>
<ul>
<li>
<p><a id="ref1"></a>[1] OP-TEE. &ldquo;About OP-TEE.&rdquo; <em>OP-TEE documentation</em>. <a href="https://optee.readthedocs.io/en/latest/general/about.html" rel="noopener noreferrer" target="_blank">Documentation</a></p>
</li>
<li>
<p><a id="ref2"></a>[2] GlobalPlatform. &ldquo;TEE System Architecture v1.3.&rdquo; <a href="https://globalplatform.org/wp-content/uploads/2022/05/GPD_SPE_009-GPD_TEE_SystemArchitecture_v1.3_PublicRelease_signed.pdf" rel="noopener noreferrer" target="_blank">Specification</a></p>
</li>
<li>
<p><a id="ref3"></a>[3] OP-TEE. &ldquo;Platforms supported.&rdquo; <em>OP-TEE documentation</em>. <a href="https://optee.readthedocs.io/en/latest/general/platforms.html" rel="noopener noreferrer" target="_blank">Documentation</a></p>
</li>
<li>
<p><a id="ref4"></a>[4] TrustedFirmware.org. &ldquo;OP-TEE moving into Trusted Firmware.&rdquo; <a href="https://www.trustedfirmware.org/blog/op-tee-moving-into-trusted-firmware/" rel="noopener noreferrer" target="_blank">Project announcement</a></p>
</li>
<li>
<p><a id="ref5"></a>[5] Qualcomm. &ldquo;Qualcomm Linux System Software Architecture.&rdquo; <a href="https://docs.qualcomm.com/bundle/publicresource/topics/80-80022-252/qualcomm-linux-sw-overview.html" rel="noopener noreferrer" target="_blank">Documentation</a></p>
</li>
<li>
<p><a id="ref6"></a>[6] Samsung. &ldquo;Samsung TEEGRIS.&rdquo; <em>Samsung Developer</em>. <a href="https://developer.samsung.com/teegris/overview.html" rel="noopener noreferrer" target="_blank">Documentation</a></p>
</li>
<li>
<p><a id="ref7"></a>[7] Apple. &ldquo;The Secure Enclave.&rdquo; <em>Apple Platform Security</em>. <a href="https://support.apple.com/guide/security-pdf/the-secure-enclave-sec59b0b31ff/web" rel="noopener noreferrer" target="_blank">Documentation</a></p>
</li>
<li>
<p><a id="ref8"></a>[8] OP-TEE. &ldquo;Security scope.&rdquo; <em>OP-TEE documentation</em>. <a href="https://optee.readthedocs.io/en/latest/general/security_scope.html" rel="noopener noreferrer" target="_blank">Documentation</a></p>
</li>
<li>
<p><a id="ref9"></a>[9] TrustedFirmware.org. &ldquo;Firmware Design.&rdquo; <em>Trusted Firmware-A documentation</em>. <a href="https://trustedfirmware-a.readthedocs.io/en/stable/design/firmware-design.html" rel="noopener noreferrer" target="_blank">Documentation</a></p>
</li>
<li>
<p><a id="ref10"></a>[10] OP-TEE. &ldquo;SHA-3 accelerated finalize heap overflow.&rdquo; <em>GHSA-75x4-j8p9-55qv / CVE-2026-40257</em>. <a href="https://github.com/OP-TEE/optee_os/security/advisories/GHSA-75x4-j8p9-55qv" rel="noopener noreferrer" target="_blank">Advisory</a></p>
</li>
<li>
<p><a id="ref11"></a>[11] OP-TEE. &ldquo;RSASSA EMSA-PKCS1-v1_5 underflow in emsa_pkcs1_v1_5_encode().&rdquo; <em>GHSA-4cf8-v5g3-73gr / CVE-2026-33662</em>. <a href="https://github.com/OP-TEE/optee_os/security/advisories/GHSA-4cf8-v5g3-73gr" rel="noopener noreferrer" target="_blank">Advisory</a></p>
</li>
<li>
<p><a id="ref12"></a>[12] IETF. &ldquo;PKCS #1: RSA Cryptography Specifications Version 2.1.&rdquo; <em>RFC 3447</em>. <a href="https://www.rfc-editor.org/rfc/rfc3447#section-9.2" rel="noopener noreferrer" target="_blank">Specification</a></p>
</li>
<li>
<p><a id="ref13"></a>[13] SecMate. &ldquo;Disclosures.&rdquo; <a href="https://secmate.dev/disclosures?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=optee-vulnerabilities-disclosure&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="optee-vulnerabilities-disclosure" data-post-category="security">Disclosure page</a></p>
</li>
</ul>
<hr>
<p><em>The SecMate Team</em></p>
]]></content:encoded><media:content url="https://blog.secmate.dev/images/og_image.jpg" medium="image"/></item><item><title>Memory Safety Vulnerabilities in Renesas FSP Cryptographic and Networking Code</title><link>https://blog.secmate.dev/posts/renesas-fsp-vulnerabilities/</link><pubDate>Thu, 23 Apr 2026 04:00:00 +0000</pubDate><atom:updated>2026-04-23T04:00:00+00:00</atom:updated><dc:creator>Maxime Rossi Bellom</dc:creator><dc:creator>Ramtine Tofighi Shirazi</dc:creator><category>Security</category><category>Vulnerability Research</category><category>Embedded</category><guid>https://blog.secmate.dev/posts/renesas-fsp-vulnerabilities/</guid><description>SecMate found seven vulnerabilities in Renesas FSP, including flaws in RSA verification, AES-XTS finalization, GCM decrypt handling, and the RMAC receive path.</description><content:encoded><![CDATA[<h1 id="memory-safety-vulnerabilities-in-renesas-fsp-cryptographic-and-networking-code">Memory Safety Vulnerabilities in Renesas FSP Cryptographic and Networking Code</h1>
<p>SecMate found seven vulnerabilities in Renesas FSP <a href="#ref1">[1]</a>, the Flexible Software Package for Renesas RA microcontrollers <a href="#ref2">[2]</a>. FSP is the vendor software stack for RA devices and bundles BSP code, HAL drivers, middleware, and security integrations, including hardware-backed cryptography and Mbed TLS support.</p>
<img src="/images/renesas-architecture.svg" alt="FSP layered architecture: BSP, HAL drivers, middleware, security modules, and RTOS support" style="width: 100%; max-width: 800px; height: auto; margin: 1.5rem auto; display: block;">
<p>In the <code>v6.1.0</code> FSP codebase we analyzed at commit <code>ed247f90fb9bc390ebd36f1a0b7847b0ff44f322</code> <a href="#ref3">[3]</a>, we found issues in RSA verification, the hardware-accelerated AES-XTS path, GCM decrypt handling, and the RMAC receive path. These are implementation flaws in FSP integration code around hardware-backed cryptography and networking. They are not flaws in AES, GCM, CCM, XTS, or RSA as algorithms.</p>
<h2 id="advisory-map">Advisory Map</h2>
<table>
<thead>
<tr>
<th>Area</th>
<th>Vulnerability</th>
<th>SecMate Advisory</th>
<th>Renesas Advisory</th>
</tr>
</thead>
<tbody>
<tr>
<td>RMAC</td>
<td>Read buffer overflow in <code>r_rmac.c</code></td>
<td><a href="https://secmate.dev/disclosures/SECMATE-2025-0021?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=renesas-fsp-vulnerabilities&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="renesas-fsp-vulnerabilities" data-post-category="security">SECMATE-2025-0021</a></td>
<td><a href="https://github.com/renesas/fsp/issues/431" rel="noopener noreferrer" target="_blank">renesas/fsp#431</a></td>
</tr>
<tr>
<td>RSA</td>
<td>Signature verification pointer underflow in <code>r_sce_rsa.c</code></td>
<td><a href="https://secmate.dev/disclosures/SECMATE-2025-0019?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=renesas-fsp-vulnerabilities&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="renesas-fsp-vulnerabilities" data-post-category="security">SECMATE-2025-0019</a></td>
<td><a href="https://github.com/renesas/fsp/issues/423" rel="noopener noreferrer" target="_blank">renesas/fsp#423</a></td>
</tr>
<tr>
<td>AES-XTS</td>
<td>Encrypt and decrypt finalization overflows in <code>aes_alt_process.c</code></td>
<td><a href="https://secmate.dev/disclosures/SECMATE-2025-0020?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=renesas-fsp-vulnerabilities&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="renesas-fsp-vulnerabilities" data-post-category="security">SECMATE-2025-0020</a></td>
<td><a href="https://github.com/renesas/fsp/issues/427" rel="noopener noreferrer" target="_blank">renesas/fsp#427</a></td>
</tr>
<tr>
<td>GCM</td>
<td>Tag length buffer overflow in <code>gcm_alt_process.c</code></td>
<td><a href="https://secmate.dev/disclosures/SECMATE-2025-0022?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=renesas-fsp-vulnerabilities&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="renesas-fsp-vulnerabilities" data-post-category="security">SECMATE-2025-0022</a></td>
<td><a href="https://github.com/renesas/fsp/issues/428" rel="noopener noreferrer" target="_blank">renesas/fsp#428</a></td>
</tr>
<tr>
<td>CCM</td>
<td>Tag length buffer overflow in <code>ccm_alt_process.c</code></td>
<td><a href="https://secmate.dev/disclosures/SECMATE-2025-0023?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=renesas-fsp-vulnerabilities&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="renesas-fsp-vulnerabilities" data-post-category="security">SECMATE-2025-0023</a></td>
<td><a href="https://github.com/renesas/fsp/issues/430" rel="noopener noreferrer" target="_blank">renesas/fsp#430</a></td>
</tr>
<tr>
<td>CCM adaptor</td>
<td>MAC length buffer overflow in <code>r_sce_AES_adapt.c</code></td>
<td><a href="https://secmate.dev/disclosures/SECMATE-2025-0024?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=renesas-fsp-vulnerabilities&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="renesas-fsp-vulnerabilities" data-post-category="security">SECMATE-2025-0024</a></td>
<td><a href="https://github.com/renesas/fsp/issues/429" rel="noopener noreferrer" target="_blank">renesas/fsp#429</a></td>
</tr>
</tbody>
</table>
<p>The AES-XTS advisory covers two findings, one in the encrypt finalization path and one in the decrypt finalization path.</p>
<h2 id="affected-builds-and-use-cases">Affected Builds and Use Cases</h2>
<p>SecMate&rsquo;s published disclosure advisories are available on the SecMate disclosures page <a href="#ref4">[4]</a>. The table above links both the SecMate advisory and the corresponding Renesas public advisory for each vulnerability group.</p>
<p>These cryptography-related findings do not apply to every RA build. In FSP, the accelerated AES and AEAD paths are compiled for devices with <code>SCE5</code>, <code>SCE5B</code>, <code>SCE7</code>, <code>SCE9</code>, or <code>RSIP</code> support, and the XTS path is excluded on <code>RA2</code> parts.</p>
<p>In downstream products, these APIs may be used for signature verification, authenticated decryption, encrypted storage, firmware update, or other security-sensitive processing. Exposure therefore depends on whether untrusted inputs can reach those wrappers.</p>
<p>The RMAC finding is separate from the cryptographic code paths. It affects products using the Ethernet receive API if application code provides a buffer smaller than the received frame.</p>
<p>Renesas PSIRT acknowledged the reports and published public advisories on GitHub.</p>
<h2 id="recommendations">Recommendations</h2>
<ul>
<li>If you use affected RA builds, review the Renesas advisories and update to <code>FSP v6.3.0</code> or later.</li>
<li>In downstream products, check whether untrusted inputs can reach FSP wrappers used for signature verification, authenticated decryption, encrypted storage, or firmware update handling.</li>
<li>Add negative tests for malformed signatures, AEAD metadata, XTS lengths, and oversized Ethernet frames.</li>
<li>If you use <code>r_rmac</code>, verify that receive buffers are sized for the largest frame your product accepts.</li>
</ul>
<h2 id="timeline">Timeline</h2>
<div style="position: relative; padding-left: 1.5rem; margin: 1.5rem 0;">
  <div style="position: absolute; left: 0.35rem; top: 0.5rem; bottom: 0.5rem; width: 2px; background: var(--color-border);"></div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-accent); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">October 20, 2025</div>
    <div style="color: var(--color-text);">SecMate reports the seven FSP vulnerabilities against commit <code>ed247f90fb9bc390ebd36f1a0b7847b0ff44f322</code> (<code>v6.1.0</code>).</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">December 18, 2025</div>
    <div style="color: var(--color-text);">Renesas releases <code>FSP v6.3.0</code>, which contains the public fixes <a href="#ref5">[5]</a>.</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">February 4, 2026</div>
    <div style="color: var(--color-text);">Renesas confirms that the FSP disclosure track covered all seven reported FSP vulnerabilities and that each would receive its own advisory.</div>
  </div>
  <div style="position: relative;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #22c55e; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">February 10, 2026</div>
    <div style="color: var(--color-text);">Renesas tells SecMate the public FSP advisories were published on GitHub.</div>
  </div>
</div>
<h2 id="takeaway">Takeaway</h2>
<p>The common thread is not broken cryptography. It is memory safety in the FSP code that exposes cryptographic and Ethernet functionality to RA applications. Hardware-backed cryptography still depends on the surrounding integration code.</p>
<p>For the full list of our disclosures, see our disclosure page <a href="#ref4">[4]</a>. If you are building embedded systems and want to find vulnerabilities before attackers do, <a href="https://secmate.dev/register?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=renesas-fsp-vulnerabilities&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_register" data-post-slug="renesas-fsp-vulnerabilities" data-post-category="security">reach out</a>.</p>
<h2 id="references">References</h2>
<ul>
<li>
<p><a id="ref1"></a>[1] Renesas. &ldquo;Renesas Flexible Software Package.&rdquo; <em>GitHub</em>. <a href="https://github.com/renesas/fsp" rel="noopener noreferrer" target="_blank">Repository</a></p>
</li>
<li>
<p><a id="ref2"></a>[2] Renesas. &ldquo;Renesas Flexible Software Package Documentation.&rdquo; <a href="https://renesas.github.io/fsp/" rel="noopener noreferrer" target="_blank">Documentation</a></p>
</li>
<li>
<p><a id="ref3"></a>[3] Renesas. &ldquo;FSP v6.1.0 analyzed commit&rdquo; (<code>ed247f90fb9bc390ebd36f1a0b7847b0ff44f322</code>). <a href="https://github.com/renesas/fsp/commit/ed247f90fb9bc390ebd36f1a0b7847b0ff44f322" rel="noopener noreferrer" target="_blank">Commit</a></p>
</li>
<li>
<p><a id="ref4"></a>[4] SecMate. &ldquo;Disclosures.&rdquo; <a href="https://secmate.dev/disclosures?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=renesas-fsp-vulnerabilities&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="renesas-fsp-vulnerabilities" data-post-category="security">Disclosure page</a></p>
</li>
<li>
<p><a id="ref5"></a>[5] Renesas. &ldquo;FSP v6.3.0.&rdquo; <em>GitHub Releases</em>. <a href="https://github.com/renesas/fsp/releases/tag/v6.3.0" rel="noopener noreferrer" target="_blank">Release</a></p>
</li>
</ul>
<hr>
<p><em>The SecMate Team</em></p>
]]></content:encoded><media:content url="https://blog.secmate.dev/images/og_image.jpg" medium="image"/></item><item><title>Rust Support in SecMate: Two CVEs in RustFS</title><link>https://blog.secmate.dev/posts/rustfs-vulnerabilities-secmate-rust-support/</link><pubDate>Wed, 04 Feb 2026 12:00:00 +0000</pubDate><atom:updated>2026-02-04T12:00:00+00:00</atom:updated><dc:creator>Maxime Rossi Bellom</dc:creator><dc:creator>Ramtine Tofighi Shirazi</dc:creator><category>Security</category><category>Vulnerability Research</category><guid>https://blog.secmate.dev/posts/rustfs-vulnerabilities-secmate-rust-support/</guid><description>SecMate's AI-powered static analysis now supports Rust. We demonstrate this with two vulnerabilities in RustFS: an IP-based authentication bypass (CVE-2026-21862) and a remote DoS via gRPC panic (CVE-2025-69255).</description><content:encoded><![CDATA[<h1 id="rust-support-in-secmate-two-cves-in-rustfs">Rust Support in SecMate: Two CVEs in RustFS</h1>
<p>Rust is often praised for its memory safety guarantees, and rightfully so: in safe Rust, buffer overflows, use-after-free, and data races are eliminated at compile time. However, memory safety is not the same as security. Logic bugs, authentication flaws, and improper error handling can still leave Rust applications vulnerable.</p>
<p>To illustrate this, we ran SecMate&rsquo;s automated analysis on RustFS <a href="#ref1">[1]</a>, an S3-compatible object storage server written in Rust. We found two issues: an authentication bypass that lets attackers spoof their IP address to circumvent access controls (CVE-2026-21862 <a href="#ref2">[2]</a>), and a remote denial-of-service caused by unhandled deserialization errors (CVE-2025-69255 <a href="#ref3">[3]</a>).</p>
<h2 id="rust-support-in-secmate">Rust Support in SecMate</h2>
<p><a href="https://secmate.dev?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=rustfs-vulnerabilities-secmate-rust-support&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_secmate" data-post-slug="rustfs-vulnerabilities-secmate-rust-support" data-post-category="security">SecMate</a> is an AI-powered static analysis tool specialized in the security of embedded software. We combine static analysis techniques with AI to find vulnerabilities that traditional tools miss: logic bugs, authentication flaws, and subtle design issues that require understanding how data flows through a system.</p>
<p>Rust is increasingly adopted in embedded and systems programming for its memory safety guarantees. Firmware, bootloaders, and security-critical components are being rewritten in Rust to eliminate entire classes of vulnerabilities. To support this trend, we recently added Rust to our analysis engine.</p>
<p>While RustFS is not an embedded application, it made an excellent first target: it is a security-sensitive system handling authentication, access control, and network protocols. This is the kind of complex logic where vulnerabilities hide regardless of the language.</p>
<h2 id="what-is-rustfs">What is RustFS?</h2>
<p>RustFS is an S3-compatible object storage server written in Rust. Think of it as a self-hosted alternative to Amazon S3: you can store and retrieve objects using the same API that countless applications already support.</p>
<p>Key features include:</p>
<ul>
<li>Full S3 API compatibility for drop-in replacement</li>
<li>IAM policies for fine-grained access control</li>
<li>gRPC interface for cluster administration and metrics</li>
<li>Support for erasure coding and distributed deployments</li>
</ul>
<p>RustFS is designed for performance and reliability. It handles sensitive data and access control decisions, making security critical.</p>
<h2 id="vulnerability-1-ip-based-authentication-bypass-cve-2026-21862">Vulnerability #1: IP-Based Authentication Bypass (CVE-2026-21862)</h2>
<p><strong>Location</strong>: <code>rustfs/src/auth.rs:294</code> (<code>get_condition_values</code>)</p>
<h3 id="the-bug">The Bug</h3>
<p>RustFS supports IAM policies that restrict access based on the client&rsquo;s IP address. This is a common pattern: allow operations only from trusted networks, block everything else.</p>
<p>This feature exists because RustFS is often deployed behind a reverse proxy or load balancer. In that setup, every TCP connection appears to come from the proxy, not the real client. To preserve the original client address, proxies add forwarding headers like <code>X-Forwarded-For</code>, and applications use them for both access control and logging. That design only works if the proxy is trusted.</p>
<p>The problem is how RustFS determines the client&rsquo;s IP. It reads forwarding headers like <code>X-Forwarded-For</code> or <code>X-Real-IP</code> and trusts them unconditionally:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">remote_addr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">header</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#34;x-forwarded-for&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">and_then</span><span class="p">(</span><span class="o">|</span><span class="n">v</span><span class="o">|</span><span class="w"> </span><span class="n">v</span><span class="p">.</span><span class="n">to_str</span><span class="p">().</span><span class="n">ok</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">and_then</span><span class="p">(</span><span class="o">|</span><span class="n">s</span><span class="o">|</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="sc">&#39;,&#39;</span><span class="p">).</span><span class="n">next</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">or_else</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="n">header</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#34;x-real-ip&#34;</span><span class="p">).</span><span class="n">and_then</span><span class="p">(</span><span class="o">|</span><span class="n">v</span><span class="o">|</span><span class="w"> </span><span class="n">v</span><span class="p">.</span><span class="n">to_str</span><span class="p">().</span><span class="n">ok</span><span class="p">()))</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">unwrap_or</span><span class="p">(</span><span class="s">&#34;127.0.0.1&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="w"></span><span class="n">args</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="s">&#34;SourceIp&#34;</span><span class="p">.</span><span class="n">to_owned</span><span class="p">(),</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="n">get_source_ip_raw</span><span class="p">(</span><span class="n">header</span><span class="p">,</span><span class="w"> </span><span class="n">remote_addr</span><span class="p">)]);</span><span class="w">
</span></span></span></code></pre></div><p>RustFS never verifies that the request actually came through a trusted proxy. Any client can set these headers directly.</p>
<h3 id="the-attack">The Attack</h3>
<p>An attacker can bypass IP-based access controls by simply adding a forged header to their request:</p>
<div id="attack-diagram" style="width: 100%; max-width: 680px; margin: 2rem auto; display: block;">
<svg viewBox="0 0 680 380" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Sequence diagram showing IP spoofing attack">
<defs>
<marker id="arrowMain" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#334155"/>
</marker>
<marker id="arrowMainDark" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#e2e8f0"/>
</marker>
<marker id="arrowGreen" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#22c55e"/>
</marker>
</defs>
<!-- Background -->
<rect width="680" height="380" rx="8" class="svg-bg"/>
<!-- Step 0: Actors (visible immediately) -->
<g class="svg-step" data-step="0">
<rect x="40" y="20" width="120" height="50" rx="6" class="svg-attacker-box" stroke-width="2"/>
<text x="100" y="42" text-anchor="middle" class="svg-attacker-text" font-family="system-ui, sans-serif" font-size="14" font-weight="600">Attacker</text>
<text x="100" y="58" text-anchor="middle" class="svg-sub-text" font-family="system-ui, sans-serif" font-size="11">(IP: 1.2.3.4)</text>
<rect x="280" y="20" width="120" height="50" rx="6" class="svg-actor-box" stroke-width="2"/>
<text x="340" y="50" text-anchor="middle" class="svg-actor-text" font-family="system-ui, sans-serif" font-size="14" font-weight="600">RustFS</text>
<rect x="520" y="20" width="120" height="50" rx="6" class="svg-actor-box" stroke-width="2"/>
<text x="580" y="50" text-anchor="middle" class="svg-actor-text" font-family="system-ui, sans-serif" font-size="14" font-weight="600">IAM Policy</text>
<line x1="100" y1="70" x2="100" y2="360" class="svg-lifeline" stroke-width="2" stroke-dasharray="6,4"/>
<line x1="340" y1="70" x2="340" y2="360" class="svg-lifeline" stroke-width="2" stroke-dasharray="6,4"/>
<line x1="580" y1="70" x2="580" y2="360" class="svg-lifeline" stroke-width="2" stroke-dasharray="6,4"/>
</g>
<!-- Step 1: HTTP Request -->
<g class="svg-step" data-step="1">
<line x1="100" y1="110" x2="330" y2="110" class="svg-msg-line svg-arrow-line" stroke-width="2" marker-end="url(#arrowMain)"/>
<text x="215" y="100" text-anchor="middle" class="svg-msg-text" font-family="system-ui, sans-serif" font-size="12">HTTP Request</text>
<text x="215" y="130" text-anchor="middle" class="svg-code-text" font-family="monospace" font-size="11">X-Forwarded-For: 10.0.0.5</text>
</g>
<!-- Step 2: Forged header note -->
<g class="svg-step" data-step="2">
<text x="100" y="160" text-anchor="middle" class="svg-sub-text" font-family="system-ui, sans-serif" font-size="10" font-style="italic">(forged header)</text>
</g>
<!-- Step 3: Policy check -->
<g class="svg-step" data-step="3">
<line x1="340" y1="195" x2="570" y2="195" class="svg-msg-line svg-arrow-line" stroke-width="2" marker-end="url(#arrowMain)"/>
<text x="455" y="185" text-anchor="middle" class="svg-msg-text" font-family="system-ui, sans-serif" font-size="12">Check: is 10.0.0.5 allowed?</text>
</g>
<!-- Step 4: Policy allows -->
<g class="svg-step" data-step="4">
<rect x="500" y="215" width="160" height="35" rx="4" class="svg-policy-box" stroke-width="1"/>
<text x="580" y="237" text-anchor="middle" class="svg-policy-text" font-family="system-ui, sans-serif" font-size="11">Policy allows 10.0.0.5/32</text>
</g>
<!-- Step 5: Allowed response -->
<g class="svg-step" data-step="5">
<line x1="570" y1="275" x2="350" y2="275" stroke="#22c55e" stroke-width="2" stroke-dasharray="6,3" marker-end="url(#arrowGreen)"/>
<text x="460" y="265" text-anchor="middle" class="svg-ok-text" font-family="system-ui, sans-serif" font-size="12" font-weight="600">Allowed</text>
</g>
<!-- Step 6: Access granted -->
<g class="svg-step" data-step="6">
<line x1="330" y1="330" x2="110" y2="330" stroke="#22c55e" stroke-width="2" stroke-dasharray="6,3" marker-end="url(#arrowGreen)"/>
<text x="220" y="320" text-anchor="middle" class="svg-ok-text" font-family="system-ui, sans-serif" font-size="12" font-weight="600">200 OK - Access Granted</text>
</g>
</svg>
</div>
<script>
(function() {
  var diagram = document.getElementById('attack-diagram');
  if (!diagram) return;
  var steps = diagram.querySelectorAll('.svg-step');
  steps.forEach(function(step) {
    if (step.dataset.step !== '0') step.style.opacity = '0';
  });
  var animated = false;
  var observer = new IntersectionObserver(function(entries) {
    entries.forEach(function(entry) {
      if (entry.isIntersecting && !animated) {
        animated = true;
        steps.forEach(function(step) {
          var delay = parseInt(step.dataset.step) * 600;
          setTimeout(function() {
            step.style.transition = 'opacity 0.5s ease-in-out';
            step.style.opacity = '1';
          }, delay);
        });
      }
    });
  }, { threshold: 0.3 });
  observer.observe(diagram);
})();
</script>
<p>Here is what happens step by step:</p>
<ol>
<li>An IAM policy allows <code>s3:ListBucket</code> only from IP <code>10.0.0.5/32</code> (an internal server)</li>
<li>The attacker connects from their external IP <code>1.2.3.4</code></li>
<li>They add the header <code>X-Forwarded-For: 10.0.0.5</code> to their request</li>
<li>RustFS reads the header and believes the client IP is <code>10.0.0.5</code></li>
<li>The IAM policy check passes, and the attacker gets access</li>
</ol>
<h3 id="impact">Impact</h3>
<p>This vulnerability bypasses IP-based access controls when headers are attacker-controlled, such as when clients connect directly to RustFS or through an untrusted proxy. If a trusted proxy controls or strips these headers before forwarding, the risk is reduced. However, in deployments where clients can reach RustFS directly, <code>aws:SourceIp</code> conditions become bypassable. An attacker can:</p>
<ul>
<li>Access buckets restricted to internal networks</li>
<li>Bypass IP allowlists meant for trusted services</li>
<li>Circumvent geographic restrictions</li>
<li>Spoof audit trails and security monitoring by forging client IPs</li>
<li>In the version we analyzed, missing headers fell back to <code>127.0.0.1</code>, so policies allowing localhost could unintentionally allow remote requests</li>
</ul>
<h3 id="fix-status">Fix Status</h3>
<p>This vulnerability has been fixed. RustFS now:</p>
<ul>
<li>Provides an environment variable (<code>_RUSTFS_API_XFF_HEADER</code>) to disable forwarding header processing entirely</li>
<li>Falls back to the actual TCP connection&rsquo;s peer address when headers are missing or disabled</li>
<li>Operators deploying RustFS without a trusted proxy should set <code>_RUSTFS_API_XFF_HEADER=off</code></li>
</ul>
<h2 id="vulnerability-2-remote-dos-via-grpc-panic-cve-2025-69255">Vulnerability #2: Remote DoS via gRPC Panic (CVE-2025-69255)</h2>
<p><strong>Location</strong>: <code>rustfs/src/storage/tonic_service.rs:1768-1785</code> (<code>get_metrics</code>)</p>
<h3 id="the-bug-1">The Bug</h3>
<p>RustFS exposes a gRPC interface for cluster operations, including a <code>GetMetrics</code> endpoint. The handler deserializes client-supplied bytes using <code>Deserialize::deserialize()</code> and calls <code>.unwrap()</code> on the result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// In NodeService::get_metrics handler
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="kd">let</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Deserialize</span>::<span class="n">deserialize</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">buf_t</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">  </span><span class="c1">// Panics on bad input!
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"></span><span class="kd">let</span><span class="w"> </span><span class="n">o</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Deserialize</span>::<span class="n">deserialize</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">buf_o</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">  </span><span class="c1">// Same problem here
</span></span></span></code></pre></div><p>In Rust, <code>.unwrap()</code> is convenient for cases where failure is truly unexpected. But here, the input comes from the network. An attacker controls what bytes get deserialized. If they send malformed data, deserialization fails, and <code>.unwrap()</code> triggers a panic, aborting the request.</p>
<p>RustFS does wrap request handling in a panic-catching layer, so a single panic usually returns an error instead of killing the whole process. That limits the blast radius, but panics are still expensive to handle.</p>
<h3 id="the-attack-1">The Attack</h3>
<p>The attack requires sending a malformed gRPC request. When a client sends an empty or invalid <code>metric_type</code> field, the server attempts to deserialize it, fails, and the <code>.unwrap()</code> call causes a panic:</p>
<p>When the malformed request arrives, the server logs a panic:</p>
<pre tabindex="0"><code>thread &#39;rustfs-worker&#39; panicked at rustfs/src/storage/tonic_service.rs:1778:66:
called `Result::unwrap()` on an `Err` value: InvalidMarkerRead(Error {
    kind: UnexpectedEof,
    message: &#34;failed to fill whole buffer&#34;
})
</code></pre><h3 id="impact-1">Impact</h3>
<p>This was a remote denial-of-service risk. The gRPC endpoint uses HMAC signature authentication via <code>x-rustfs-signature</code> and <code>x-rustfs-timestamp</code> headers. A single bad request only affected that one request, but repeated exploitation could cause a real denial-of-service. An attacker with valid credentials could send many malformed requests (or parallelize them across connections) to repeatedly trigger panics, which:</p>
<ul>
<li>Consumed CPU time in panic handling and stack unwinding</li>
<li>Flooded logs with panic traces and slowed down observability pipelines</li>
<li>Starved gRPC worker threads so <code>GetMetrics</code> became effectively unavailable</li>
<li>Could slow other gRPC handlers that share the same runtime under high load</li>
</ul>
<p>The fix was straightforward: replace <code>.unwrap()</code> with proper error handling that returns an error response instead of panicking:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Fixed version
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kd">let</span><span class="w"> </span><span class="n">t</span>: <span class="nc">MetricType</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">Deserialize</span>::<span class="n">deserialize</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">buf_t</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="nb">Ok</span><span class="p">(</span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="nb">Err</span><span class="p">(</span><span class="n">err</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">Response</span>::<span class="n">new</span><span class="p">(</span><span class="n">GetMetricsResponse</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">            </span><span class="n">success</span>: <span class="nc">false</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">            </span><span class="n">error_info</span>: <span class="nb">Some</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;Invalid metric_type: </span><span class="si">{err}</span><span class="s">&#34;</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">            </span><span class="o">..</span><span class="nb">Default</span>::<span class="n">default</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="p">}));</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"></span><span class="p">};</span><span class="w">
</span></span></span></code></pre></div><h3 id="fix-status-1">Fix Status</h3>
<p>This vulnerability has been fixed in current RustFS versions. The <code>.unwrap()</code> calls are gone, and deserialization errors now return a response with <code>success=false</code> and an error string instead of panicking. The fix includes tests to ensure invalid <code>metric_type</code> and <code>opts</code> values do not cause panics.</p>
<h2 id="the-rust-security-reality">The Rust Security Reality</h2>
<p>Both vulnerabilities highlight an important point: <strong>Rust&rsquo;s safety guarantees have limits</strong>.</p>
<p>In safe Rust, entire classes of memory-safety bugs are eliminated at compile time. You will not find buffer overflows, use-after-free, or data races in safe Rust code. This is a huge win for security.</p>
<p>But Rust does not prevent:</p>
<ul>
<li><strong>Logic bugs</strong>: Trusting user-controlled headers for authentication decisions</li>
<li><strong>Error handling mistakes</strong>: Using <code>.unwrap()</code> on fallible operations with untrusted input</li>
<li><strong>Design flaws</strong>: Exposing sensitive endpoints without proper authentication</li>
<li><strong>Cryptographic errors</strong>: Using weak algorithms or improper key management</li>
</ul>
<blockquote class="admonition admonition-insight">
<strong>Key Insight</strong>
<p>Memory safety is necessary but not sufficient for security. Rust eliminates one category of vulnerabilities, but logic bugs and design flaws require the same careful analysis as in any other language.</p>
</blockquote>
<p>These findings demonstrate why AI-assisted code analysis remains valuable even for memory-safe languages. SecMate identified these vulnerabilities by analyzing data flow and reasoning about how untrusted input affects security decisions, something that applies regardless of the language.</p>
<h2 id="disclosure-timeline">Disclosure Timeline</h2>
<div style="position: relative; padding-left: 1.5rem; margin: 1.5rem 0;">
  <div style="position: absolute; left: 0.35rem; top: 0.5rem; bottom: 0.5rem; width: 2px; background: var(--color-border);"></div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-accent); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Dec 8, 2025</div>
    <div style="color: var(--color-text);">SecMate reported both vulnerabilities to RustFS maintainers</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Dec 28, 2025</div>
    <div style="color: var(--color-text);">Maintainers acknowledged and confirmed the issues</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #22c55e; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Dec 30, 2025</div>
    <div style="color: var(--color-text);">Patches merged and released</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #f59e0b; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Jan 7, 2026</div>
    <div style="color: var(--color-text);">CVE-2025-69255 published (<a href="https://github.com/rustfs/rustfs/security/advisories/GHSA-gw2x-q739-qhcr">GHSA-gw2x-q739-qhcr — RustFS gRPC GetMetrics deserialization panic enables remote DoS</a>)</div>
  </div>
  <div style="position: relative;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #f59e0b; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Feb 3, 2026</div>
    <div style="color: var(--color-text);">CVE-2026-21862 published (<a href="https://github.com/rustfs/rustfs/security/advisories/GHSA-fc6g-2gcp-2qrq">GHSA-fc6g-2gcp-2qrq — SourceIp bypass via spoofed X-Forwarded-For/Real-IP headers</a>)</div>
  </div>
</div>
<p>We thank the RustFS maintainers for their responsiveness in addressing these issues. Coordinated disclosure helps keep the ecosystem secure.</p>
<h2 id="want-to-know-more">Want to Know More?</h2>
<p>These two CVEs are the first vulnerabilities we are publicly disclosing in a Rust codebase, showcasing SecMate&rsquo;s expanded language support. As Rust adoption grows in systems programming and embedded development, we expect to find more issues like these: logical flaws that memory safety alone cannot prevent.</p>
<p>Interested in our vulnerability research? Browse our <a href="https://secmate.dev/disclosures?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=rustfs-vulnerabilities-secmate-rust-support&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="rustfs-vulnerabilities-secmate-rust-support" data-post-category="security">public disclosures</a> to see what else we have found. If you want SecMate to analyze your codebase, whether it is Rust, C, C++, or another language, <a href="https://secmate.dev/register?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=rustfs-vulnerabilities-secmate-rust-support&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_register" data-post-slug="rustfs-vulnerabilities-secmate-rust-support" data-post-category="security">get in touch</a>.</p>
<h2 id="references">References</h2>
<ul>
<li>
<p><a id="ref1"></a>[1] RustFS. &ldquo;RustFS - High Performance Object Storage&rdquo; <em>GitHub</em>. <a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer" target="_blank">Repository</a> | <a href="https://docs.rustfs.com/" rel="noopener noreferrer" target="_blank">Documentation</a></p>
</li>
<li>
<p><a id="ref2"></a>[2] GitHub. &ldquo;GHSA-fc6g-2gcp-2qrq - SourceIp bypass via spoofed X-Forwarded-For/Real-IP headers&rdquo; <em>GitHub Security Advisory</em>. <a href="https://github.com/rustfs/rustfs/security/advisories/GHSA-fc6g-2gcp-2qrq" rel="noopener noreferrer" target="_blank">Advisory</a></p>
</li>
<li>
<p><a id="ref3"></a>[3] GitHub. &ldquo;GHSA-gw2x-q739-qhcr - RustFS gRPC GetMetrics deserialization panic enables remote DoS&rdquo; <em>GitHub Security Advisory</em>. <a href="https://github.com/rustfs/rustfs/security/advisories/GHSA-gw2x-q739-qhcr" rel="noopener noreferrer" target="_blank">Advisory</a></p>
</li>
</ul>
<hr>
<p><em>The SecMate Team</em></p>
]]></content:encoded><media:content url="https://blog.secmate.dev/images/og_image.jpg" medium="image"/></item><item><title>How SecMate Discovered two Vulnerabilities in libcoap</title><link>https://blog.secmate.dev/posts/libcoap-vulnerabilities-disclosure/</link><pubDate>Wed, 14 Jan 2026 00:00:00 +0000</pubDate><atom:updated>2026-01-14T00:00:00+00:00</atom:updated><dc:creator>Maxime Rossi Bellom</dc:creator><dc:creator>Ramtine Tofighi Shirazi</dc:creator><category>Security</category><category>Vulnerability Research</category><category>IoT</category><guid>https://blog.secmate.dev/posts/libcoap-vulnerabilities-disclosure/</guid><description>SecMate's AI-powered static analysis tool discovered two vulnerabilities in libcoap: an out-of-bounds write in address resolution (CVE-2025-34468) and an out-of-bounds read in OSCORE parsing (CVE-2025-59391). Here's the technical breakdown.</description><content:encoded><![CDATA[<h1 id="how-secmate-discovered-vulnerabilities-in-libcoap">How SecMate Discovered Vulnerabilities in libcoap</h1>
<p>We discovered two memory-safety issues in libcoap, a widely deployed IoT library. The first is an out-of-bounds write in a fixed buffer reachable via proxy requests that can crash servers with a single UDP packet. The second is an out-of-bounds read in OSCORE configuration parsing that can crash the parser on malformed input. Both vulnerabilities have amplified impact on the constrained embedded devices the library was built for.</p>
<h2 id="about-secmate">About SecMate</h2>
<p><a href="https://secmate.dev?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=libcoap-vulnerabilities-disclosure&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_secmate" data-post-slug="libcoap-vulnerabilities-disclosure" data-post-category="security">SecMate</a> combines static analysis with AI to find security vulnerabilities in source code, specifically in embedded devices and low-level code.</p>
<p>So far, we have reported <strong>more than 60 vulnerabilities</strong> across embedded and systems projects, most still under <a href="https://secmate.dev/disclosures?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=libcoap-vulnerabilities-disclosure&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="libcoap-vulnerabilities-disclosure" data-post-category="security">coordinated disclosure</a>.</p>
<p>Today, we are sharing two of our findings in libcoap (the first two CVEs uncovered with SecMate), a widely-used CoAP implementation for IoT devices.</p>
<h2 id="what-is-libcoap">What is libcoap?</h2>
<p>libcoap <a href="#ref1">[1]</a> is a C implementation of the Constrained Application Protocol (CoAP), standardized as RFC 7252 <a href="#ref2">[2]</a>. Think of CoAP as &ldquo;HTTP for IoT&rdquo;: a lightweight protocol for devices that can&rsquo;t afford the overhead of full HTTP/TCP stacks.</p>
<p>The library is mature and widely deployed:</p>
<ul>
<li>Supports multiple TLS backends (OpenSSL, GnuTLS, Mbed TLS, wolfSSL)</li>
<li>Runs on everything from Linux servers to ESP32 microcontrollers</li>
<li>Used in smart home devices, industrial sensors, and critical infrastructure</li>
</ul>
<p>Here is the catch: the devices running libcoap are often <em>constrained</em>. Limited memory, no MMU, no ASLR, no stack canaries. When you find a memory-safety issue in this context, the impact is amplified.</p>
<h2 id="vulnerability-1-static-buffer-overflow-in-address-resolution-cve-2025-34468-3ref3">Vulnerability #1: Static Buffer Overflow in Address Resolution (CVE-2025-34468 <a href="#ref3">[3]</a>)</h2>
<p><strong>Location</strong>: <code>coap_resolve_address_info()</code> in <code>src/coap_address.c</code></p>
<h3 id="the-bug">The Bug</h3>
<p>This vulnerability affects libcoap servers running in <strong>proxy mode</strong>. The vulnerable function resolves hostnames for CoAP proxy requests, copying the hostname into a fixed 256-byte static buffer without checking the length:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">static</span> <span class="kt">char</span> <span class="n">addrstr</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>  <span class="c1">// Fixed-size static buffer
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="nf">memset</span><span class="p">(</span><span class="n">addrstr</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">addrstr</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">address</span> <span class="o">&amp;&amp;</span> <span class="n">address</span><span class="o">-&gt;</span><span class="n">length</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="nf">memcpy</span><span class="p">(</span><span class="n">addrstr</span><span class="p">,</span> <span class="n">address</span><span class="o">-&gt;</span><span class="n">s</span><span class="p">,</span> <span class="n">address</span><span class="o">-&gt;</span><span class="n">length</span><span class="p">);</span>  <span class="c1">// No bounds check!
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span><span class="k">else</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="nf">memcpy</span><span class="p">(</span><span class="n">addrstr</span><span class="p">,</span> <span class="s">&#34;localhost&#34;</span><span class="p">,</span> <span class="mi">9</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="nf">getaddrinfo</span><span class="p">(</span><span class="n">addrstr</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">hints</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">res</span><span class="p">);</span>
</span></span></code></pre></div><p>The <code>address-&gt;length</code> field comes directly from parsing CoAP options. An attacker controls this value.</p>
<h3 id="the-attack">The Attack</h3>
<p>When a CoAP server runs in proxy mode, it accepts <code>Proxy-Uri</code> options that specify where to forward requests. The hostname from this URI flows directly into the vulnerable <code>memcpy()</code>:</p>
<ol>
<li>A CoAP request arrives with a <code>Proxy-Uri</code> option containing an oversized hostname (&gt;256 bytes)</li>
<li>The proxy extracts the hostname and passes it to <code>coap_resolve_address_info()</code></li>
<li>The function calls <code>memcpy(addrstr, address-&gt;s, address-&gt;length)</code> without bounds checking</li>
<li>The fixed-size buffer overflows, corrupting adjacent memory</li>
<li>The server crashes or misbehaves; the exact impact depends on layout and mitigations</li>
</ol>
<h3 id="impact">Impact</h3>
<p>On a standard Linux system, this bug is likely to crash the server (Denial of Service). On embedded devices without memory protection, memory corruption can have broader consequences, but practical exploitation depends on layout and mitigations. One UDP packet, no authentication required when proxy mode is enabled.</p>
<h2 id="vulnerability-2-out-of-bounds-read-in-oscore-parsing-cve-2025-59391-4ref4">Vulnerability #2: Out-of-Bounds Read in OSCORE Parsing (CVE-2025-59391 <a href="#ref4">[4]</a>)</h2>
<p><strong>Location</strong>: <code>get_split_entry()</code> in <code>src/coap_oscore.c</code></p>
<h3 id="the-bug-1">The Bug</h3>
<p>OSCORE (Object Security for Constrained RESTful Environments) is a security layer for CoAP. libcoap parses OSCORE configuration files that include boolean fields. The parsing code uses <code>memcmp()</code> with a user-controlled length:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">case</span> <span class="nl">COAP_ENC_BOOL</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">memcmp</span><span class="p">(</span><span class="s">&#34;true&#34;</span><span class="p">,</span> <span class="n">begin</span><span class="p">,</span> <span class="n">end</span> <span class="o">-</span> <span class="n">begin</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="n">value</span><span class="o">-&gt;</span><span class="n">u</span><span class="p">.</span><span class="n">value_int</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="nf">memcmp</span><span class="p">(</span><span class="s">&#34;false&#34;</span><span class="p">,</span> <span class="n">begin</span><span class="p">,</span> <span class="n">end</span> <span class="o">-</span> <span class="n">begin</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">        <span class="n">value</span><span class="o">-&gt;</span><span class="n">u</span><span class="p">.</span><span class="n">value_int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span></code></pre></div><p>The problem: <code>end - begin</code> comes from the configuration file. If an attacker provides a value like <code>trueAAAAAAAA...</code> (thousands of characters), the <code>memcmp()</code> reads past the static string <code>&quot;true&quot;</code> into adjacent memory.</p>
<h3 id="impact-1">Impact</h3>
<p>This issue requires the ability to provide malicious OSCORE configuration input, which is typically a local or trusted provisioning path rather than a network-facing vector. The realistic outcome is a crash or parse failure on malformed configs.</p>
<h3 id="theoretical-oracle-attack">Theoretical Oracle Attack</h3>
<p>In theory, if a local attacker could repeatedly provide different config files and observe success/failure signals from the parser, they could use this as an oracle to infer adjacent <code>.rodata</code> bytes one at a time. The diagram below illustrates what such an attack would look like:</p>
<svg viewBox="0 0 440 100" style="width: 100%; max-width: 440px; height: auto; margin: 1.5rem auto; display: block; font-family: monospace;">
  <style>
    .svg-label { fill: var(--color-text-secondary); }
    .svg-bg { fill: var(--color-muted-bg); stroke: var(--color-border); }
    .svg-known { fill: color-mix(in srgb, var(--color-accent) 30%, var(--color-bg-card)); stroke: var(--color-accent); }
    .svg-unknown { fill: var(--color-bg-card); stroke: var(--color-border); }
    .svg-text { fill: var(--color-text); }
    .svg-text-muted { fill: var(--color-text-secondary); }
  </style>
  <text x="220" y="14" text-anchor="middle" class="svg-label" font-size="11">memory layout</text>
  <rect x="10" y="22" width="420" height="44" rx="4" class="svg-bg" stroke-width="1"/>
  <rect x="15" y="26" width="36" height="36" rx="3" class="svg-known" stroke-width="2"/><text x="33" y="50" text-anchor="middle" class="svg-text" font-size="15">t</text>
  <rect x="56" y="26" width="36" height="36" rx="3" class="svg-known" stroke-width="2"/><text x="74" y="50" text-anchor="middle" class="svg-text" font-size="15">r</text>
  <rect x="97" y="26" width="36" height="36" rx="3" class="svg-known" stroke-width="2"/><text x="115" y="50" text-anchor="middle" class="svg-text" font-size="15">u</text>
  <rect x="138" y="26" width="36" height="36" rx="3" class="svg-known" stroke-width="2"/><text x="156" y="50" text-anchor="middle" class="svg-text" font-size="15">e</text>
  <rect x="179" y="26" width="36" height="36" rx="3" class="svg-known" stroke-width="2"/><text x="197" y="50" text-anchor="middle" class="svg-text" font-size="12">\0</text>
  <rect x="220" y="26" width="36" height="36" rx="3" class="svg-unknown" stroke-width="1"><animate attributeName="fill" values="var(--color-bg-card);#7f1d1d;#166534;#166534;#166534;#166534" dur="6s" repeatCount="indefinite"/><animate attributeName="stroke" values="var(--color-border);#dc2626;#22c55e;#22c55e;#22c55e;#22c55e" dur="6s" repeatCount="indefinite"/></rect>
  <text x="238" y="50" text-anchor="middle" class="svg-text-muted" font-size="15"><animate attributeName="opacity" values="1;1;0;0;0;0" dur="6s" repeatCount="indefinite"/>?</text>
  <text x="238" y="50" text-anchor="middle" class="svg-text" font-size="15"><animate attributeName="opacity" values="0;0;1;1;1;1" dur="6s" repeatCount="indefinite"/>f</text>
  <rect x="261" y="26" width="36" height="36" rx="3" class="svg-unknown" stroke-width="1"><animate attributeName="fill" values="var(--color-bg-card);var(--color-bg-card);var(--color-bg-card);#7f1d1d;#166534;#166534" dur="6s" repeatCount="indefinite"/><animate attributeName="stroke" values="var(--color-border);var(--color-border);var(--color-border);#dc2626;#22c55e;#22c55e" dur="6s" repeatCount="indefinite"/></rect>
  <text x="279" y="50" text-anchor="middle" class="svg-text-muted" font-size="15"><animate attributeName="opacity" values="1;1;1;1;0;0" dur="6s" repeatCount="indefinite"/>?</text>
  <text x="279" y="50" text-anchor="middle" class="svg-text" font-size="15"><animate attributeName="opacity" values="0;0;0;0;1;1" dur="6s" repeatCount="indefinite"/>a</text>
  <rect x="302" y="26" width="36" height="36" rx="3" class="svg-unknown" stroke-width="1"/><text x="320" y="50" text-anchor="middle" class="svg-text-muted" font-size="15">?</text>
  <rect x="343" y="26" width="36" height="36" rx="3" class="svg-unknown" stroke-width="1"/><text x="361" y="50" text-anchor="middle" class="svg-text-muted" font-size="15">?</text>
  <rect x="384" y="26" width="36" height="36" rx="3" class="svg-unknown" stroke-width="1"/><text x="402" y="50" text-anchor="middle" class="svg-text-muted" font-size="15">?</text>
  <text x="135" y="88" text-anchor="start" font-size="15" font-weight="bold" class="svg-label">true\0</text>
  <text x="200" y="88" text-anchor="start" font-size="15" font-weight="bold" class="svg-label"><animate attributeName="opacity" values="1;0;0;0;0;0" dur="6s" calcMode="discrete" repeatCount="indefinite"/>...</text>
  <text x="200" y="88" text-anchor="start" font-size="15" font-weight="bold" fill="#dc2626"><animate attributeName="opacity" values="0;1;0;0;0;0" dur="6s" calcMode="discrete" repeatCount="indefinite"/>Y ✗</text>
  <text x="200" y="88" text-anchor="start" font-size="15" font-weight="bold" fill="#22c55e"><animate attributeName="opacity" values="0;0;1;0;0;0" dur="6s" calcMode="discrete" repeatCount="indefinite"/>f ✓</text>
  <text x="200" y="88" text-anchor="start" font-size="15" font-weight="bold" fill="#dc2626"><animate attributeName="opacity" values="0;0;0;1;0;0" dur="6s" calcMode="discrete" repeatCount="indefinite"/>fK ✗</text>
  <text x="200" y="88" text-anchor="start" font-size="15" font-weight="bold" fill="#22c55e"><animate attributeName="opacity" values="0;0;0;0;1;0" dur="6s" calcMode="discrete" repeatCount="indefinite"/>fa ✓</text>
  <text x="200" y="88" text-anchor="start" font-size="15" font-weight="bold" fill="#22c55e"><animate attributeName="opacity" values="0;0;0;0;0;1" dur="6s" calcMode="discrete" repeatCount="indefinite"/>fa</text>
</svg>
<p>However, this attack is impractical: OSCORE configuration is loaded from local files, so an attacker would need repeated local access to provide different config files and observe results. At that point, they likely have access that makes this leak scenario moot.</p>
<blockquote class="admonition admonition-insight">
<strong>Why This Bug is Easy to Miss</strong>
<p>This vulnerability is subtle: the out-of-bounds read in <code>memcmp()</code> may not crash or produce obvious symptoms during normal operation. Spotting it requires reasoning about how data flows through multiple operations, something traditional static analysis tools typically miss.</p>
</blockquote>
<h2 id="the-embedded-security-challenge">The Embedded Security Challenge</h2>
<p>Both vulnerabilities share a common theme: <strong>they are worse on the devices libcoap is designed for</strong>.</p>
<p>Desktop and server systems have decades of hardening:</p>
<ul>
<li>ASLR randomizes memory layout</li>
<li>Stack canaries detect buffer overflows</li>
<li>DEP/NX prevents code execution from data segments</li>
<li>Sandboxing limits blast radius</li>
</ul>
<p>Constrained embedded devices often have none of these. A microcontroller running FreeRTOS or bare metal has:</p>
<ul>
<li>Flat, predictable memory layout</li>
<li>No memory protection unit (MPU) or it is disabled for performance</li>
<li>No operating system to enforce isolation</li>
<li>Direct hardware access for any code that runs</li>
</ul>
<p>This isn&rsquo;t a criticism of libcoap. It is the reality of their target environment. But it means bugs like these deserve extra scrutiny.</p>
<blockquote class="admonition admonition-insight">
<strong>Key Insight</strong>
<p>When auditing code for constrained devices, assume the worst-case exploitation scenario. Mitigations you take for granted on desktop systems simply don't exist.</p>
</blockquote>
<h2 id="disclosure-and-fix">Disclosure and Fix</h2>
<p>We reported both vulnerabilities to the libcoap maintainers through coordinated disclosure. The team responded quickly and professionally, and fixes are now available in version 4.3.5a.</p>
<h3 id="disclosure-timeline">Disclosure Timeline</h3>
<div style="position: relative; padding-left: 1.5rem; margin: 1.5rem 0;">
  <div style="position: absolute; left: 0.35rem; top: 0.5rem; bottom: 0.5rem; width: 2px; background: var(--color-border);"></div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-accent); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Sep 3, 2025</div>
    <div style="color: var(--color-text);">SecMate reported OSCORE out-of-bounds read to libcoap security team</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Sep 3, 2025</div>
    <div style="color: var(--color-text);">Maintainer acknowledged and confirmed the issue is reproducible</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-accent); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Sep 10, 2025</div>
    <div style="color: var(--color-text);">SecMate reported static buffer overflow to libcoap security team</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Sep 10, 2025</div>
    <div style="color: var(--color-text);">Maintainer acknowledged the second vulnerability</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #22c55e; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Sep 15, 2025</div>
    <div style="color: var(--color-text);">Fix for OSCORE issue merged (<a href="https://github.com/obgm/libcoap/pull/1730">PR #1730</a>)</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #22c55e; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Sep 15, 2025</div>
    <div style="color: var(--color-text);">Fix for static buffer overflow merged (<a href="https://github.com/obgm/libcoap/pull/1737">PR #1737</a>)</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Sep 15, 2025</div>
    <div style="color: var(--color-text);">CVE requests submitted to MITRE for both issues</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #22c55e; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Nov 27, 2025</div>
    <div style="color: var(--color-text);">libcoap v4.3.5a released with patches for both vulnerabilities</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #f59e0b; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Dec 8, 2025</div>
    <div style="color: var(--color-text);">CVE-2025-59391 assigned by MITRE <span style="color: var(--color-text-secondary);">(85 days after request)</span></div>
  </div>
  <div style="position: relative;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #f59e0b; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">Dec 31, 2025</div>
    <div style="color: var(--color-text);">CVE-2025-34468 assigned by <a href="https://vulncheck.com/">VulnCheck</a> <span style="color: var(--color-text-secondary);">(same day, MITRE never responded)</span></div>
  </div>
</div>
<p>We want to thank the libcoap team, particularly Jon Shallow, for their responsiveness in addressing these issues. The maintainer responded on the same day for both reports. Maintaining open-source security infrastructure is often thankless work, and their quick turnaround made the ecosystem safer.</p>
<h2 id="whats-next">What&rsquo;s Next</h2>
<p>These two CVEs are just a sample of what SecMate has found. We&rsquo;re continuing to analyze embedded codebases and report vulnerabilities through coordinated disclosure.</p>
<p>If you&rsquo;re curious about our other findings, check out our <a href="https://secmate.dev/disclosures?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=libcoap-vulnerabilities-disclosure&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="libcoap-vulnerabilities-disclosure" data-post-category="security">disclosure page</a>. And if you&rsquo;re building embedded systems and want to find vulnerabilities before attackers do, <a href="https://secmate.dev/register?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=libcoap-vulnerabilities-disclosure&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_register" data-post-slug="libcoap-vulnerabilities-disclosure" data-post-category="security">reach out</a>.</p>
<h2 id="references">References</h2>
<ul>
<li>
<p><a id="ref1"></a>[1] libcoap. &ldquo;libcoap - C-Implementation of CoAP&rdquo; <em>GitHub</em>. <a href="https://github.com/obgm/libcoap" rel="noopener noreferrer" target="_blank">Repository</a></p>
</li>
<li>
<p><a id="ref2"></a>[2] IETF. &ldquo;RFC 7252 - The Constrained Application Protocol (CoAP)&rdquo; <em>IETF Datatracker</em>, June 2014. <a href="https://datatracker.ietf.org/doc/html/rfc7252" rel="noopener noreferrer" target="_blank">RFC</a></p>
</li>
<li>
<p><a id="ref3"></a>[3] NVD. &ldquo;CVE-2025-34468 - Static Buffer Overflow in libcoap&rdquo; <em>National Vulnerability Database</em>. <a href="https://nvd.nist.gov/vuln/detail/CVE-2025-34468" rel="noopener noreferrer" target="_blank">CVE</a></p>
</li>
<li>
<p><a id="ref4"></a>[4] NVD. &ldquo;CVE-2025-59391 - Out-of-Bounds Read in libcoap&rdquo; <em>National Vulnerability Database</em>. <a href="https://nvd.nist.gov/vuln/detail/CVE-2025-59391" rel="noopener noreferrer" target="_blank">CVE</a></p>
</li>
</ul>
<hr>
<p><em>The SecMate Team</em></p>
]]></content:encoded><media:content url="https://blog.secmate.dev/images/og_image.jpg" medium="image"/></item></channel></rss>