Skip to content

Latest commit

 

History

History
712 lines (666 loc) · 38.4 KB

File metadata and controls

712 lines (666 loc) · 38.4 KB
Billions of everyday devices — from smart home sensors and medical implants to industrial controllers and payment terminals — are powered by low-cost microcontrollers (MCUs). To protect sensitive operations such as cryptographic key storage and secure boot on these devices, modern Arm-based MCUs include a hardware security feature called TrustZone-M, which creates an isolated Secure World that is supposed to remain hidden even when the rest of the device software is fully compromised. Prior work had even concluded that the simple architecture of microcontrollers makes them "immune" to interrupt-based side-channel attacks. M-Step overturns this assumption.
24 exploitable vulnerabilities
in Mbed TLS
100% RSA key recovery
single-trace attack
1B+ Cortex-M chips
shipped annually
0 hardware required —
fully software-based

The First Single-Stepping Framework for Arm TrustZone-M

M-Step is the first framework enabling single-stepping — instruction-granular observation — inside TrustZone-M's Secure World. By carefully profiling previously undocumented Cortex-M interrupt-handling behavior, M-Step allows an attacker controlling the untrusted operating system to pause secure-world execution one instruction at a time and measure tiny timing differences that reveal what the secret code is doing — all in software, with no physical access or debug probes required.

This technique — interrupt-driven single-stepping — was previously well-established for high-end processors (powering servers, phones, and cloud VMs) via frameworks such as SGX-Step (Intel SGX) and SEV-Step (AMD SEV), enabling 48+ published attacks. M-Step brings the same capability to the billions of microcontrollers powering the Internet of Things.

The core challenge on Cortex-M is that the interrupt-handling mechanism is highly optimized for real-time responsiveness: multi-cycle instructions may be abandoned or paused mid-execution to reduce latency, making single-stepping non-trivial. M-Step overcomes this by profiling three distinct instruction behaviors that were not previously documented:

Atomic instructions

Cannot be interrupted during execution — the processor defers the interrupt until the instruction completes. M-Step schedules the interrupt immediately afterwards to advance exactly one instruction.

Resumable instructions

Can be interrupted mid-execution and then resumed from the interruption point. M-Step exploits this to achieve precise intra-instruction observations.

Restartable instructions

Are restarted from scratch when interrupted. M-Step times its interrupt to the final execution cycle so the instruction completes and the victim advances by exactly one step.

Instruction Interrupt (IRQ) Handler (ISR) Resume / restart
Atomic
deferred — completes first
Resumable
suspended, then resumed
Restartable
abandoned & restarted
▶ Replay

How Cortex-M handles an interrupt during instruction execution. Each row shows one of the three instruction classes discovered by M-Step. The red bar marks when the IRQ fires. Atomic instructions defer it and complete before the handler runs; resumable instructions suspend mid-execution and pick up where they left off (blue); restartable instructions are abandoned entirely and reissued from scratch. M-Step exploits these behaviors to advance the victim program by exactly one instruction per interrupt — enabling instruction-granular side-channel observation.

<script> (function(){ var S=310; var defs=[ {id:'tl-atomic',seq:[ {k:'exec',t:0},{k:'exec',t:S},{k:'exec',t:2*S}, {k:'irq',t:2.6*S}, {k:'isr',t:3*S},{k:'isr',t:4*S}, {k:'sp',t:5*S},{k:'next',t:5*S} ]}, {id:'tl-resumable',seq:[ {k:'exec',t:0}, {k:'irq',t:0.6*S}, {k:'isr',t:S},{k:'isr',t:2*S},{k:'isr',t:3*S}, {k:'resume',t:4*S},{k:'exec',t:4.5*S}, {k:'next',t:5.5*S} ]}, {id:'tl-restartable',seq:[ {k:'exec',t:0}, {k:'irq',t:0.6*S}, {k:'isr',t:S},{k:'isr',t:2*S},{k:'isr',t:3*S}, {k:'exec',t:4*S},{k:'exec',t:4.6*S},{k:'exec',t:5*S}, {k:'next',t:5.8*S} ]} ]; var timers=[]; function build(){ defs.forEach(function(d){ var row=document.getElementById(d.id); if(!row)return; var track=row.querySelector('.tl-track'); track.innerHTML=''; d.seq.forEach(function(c){ var el=document.createElement('div'); el.className=(c.k==='irq'?'tl-irq-bar':'tl-cell c-'+c.k); el.dataset.t=c.t; track.appendChild(el); }); }); } function play(){ timers.forEach(clearTimeout);timers=[]; document.querySelectorAll('#tlDemo .tl-cell,#tlDemo .tl-irq-bar').forEach(function(el){el.classList.remove('tl-vis');}); document.querySelectorAll('#tlDemo .tl-cell,#tlDemo .tl-irq-bar').forEach(function(el){ timers.push(setTimeout(function(){el.classList.add('tl-vis');},+el.dataset.t||0)); }); } document.addEventListener('DOMContentLoaded',function(){ build();setTimeout(play,600); var btn=document.getElementById('tlPlayBtn'); if(btn)btn.addEventListener('click',play); }); })(); </script>

By dynamically adapting its interrupt timing to whichever instruction type the victim is executing, M-Step achieves reliable, deterministic single-stepping of arbitrary production code on Arm Cortex-M33 microcontrollers — without any prior knowledge of the victim binary.

Breaking Real-World Cryptography: Attacking Mbed TLS

Mbed TLS is the dominant open-source cryptographic library for embedded IoT devices, developed by Arm and used in billions of devices. It is also the cryptographic backbone of Trusted Firmware-M (TF-M), Arm's official reference implementation for TrustZone-M — meaning it runs inside the Secure World on a wide range of production devices.

Using M-Step, we identified 24 exploitable call sites in the latest Mbed TLS release (v3.6.3.1). The root cause is a non-constant-time implementation of the Binary Extended Euclidean Algorithm (BEEA) used in modular arithmetic: secret-dependent control-flow decisions in this algorithm become directly observable under M-Step's instruction-granular view.

We demonstrate a single-trace attack that recovers a full RSA private key — the long-term cryptographic secret used to authenticate a device, sign firmware, or decrypt communications — from a single execution of RSA key generation or key import, with no repeated measurements required. Strikingly, even devices equipped with dedicated hardware crypto accelerators remain affected, as 7 of the vulnerable call sites leak through software fallback code.

Secure keypad
_ _ _ _
1 2 3 4 5 6 7 8 9 CLR 0 OK
M-Step interrupt-latency trace
0instructions →

Try it: press any key on the simulated secure keypad (or use your keyboard 0–9). M-Step measures the interrupt latency of each instruction executed inside the TrustZone-M secure world. The resulting trace is unique to each keypress — secret-dependent branches in the PIN-checking code leave a distinct timing signature, letting an attacker recover the PIN one digit at a time without any knowledge of the secret.

<script> (function(){ // Canvas layout constants var ML=36,MR=6,MT=10,MB=22; // margins: left, right, top, bottom var YMAX=34; // max cycle count shown on y-axis

var canvas=document.getElementById('kdCanvas'); var ctx=canvas&&canvas.getContext('2d'); var animId=null,pin=[],TRACES={};

function pw(){return canvas.width-ML-MR;} function ph(){return canvas.height-MT-MB;} // Map cycle count → canvas y-coordinate (0 cycles = bottom, YMAX = top) function toY(v){return MT+ph()*(1-v/YMAX);}

// ─ Axes and grid ─────────────────────────────────────────── function drawAxes(){ var W=canvas.width,H=canvas.height; ctx.fillStyle='#080f08'; ctx.fillRect(0,0,W,H);

// Horizontal grid lines + y-axis labels at each 10-cycle mark
ctx.font='10px monospace';
ctx.textAlign='right';
[10,20,30].forEach(function(c){
  var y=toY(c);
  // grid line
  ctx.strokeStyle='rgba(35,75,35,0.5)';
  ctx.lineWidth=1;ctx.setLineDash([]);
  ctx.beginPath();ctx.moveTo(ML,y);ctx.lineTo(W-MR,y);ctx.stroke();
  // label
  ctx.fillStyle='rgba(80,155,80,0.75)';
  ctx.fillText(c,ML-3,y+3);
});

// Dashed baseline reference at 10 cycles
var ybase=toY(10);
ctx.setLineDash([3,4]);
ctx.strokeStyle='rgba(0,200,80,0.18)';
ctx.lineWidth=1;
ctx.beginPath();ctx.moveTo(ML,ybase);ctx.lineTo(W-MR,ybase);ctx.stroke();
ctx.setLineDash([]);

// Y-axis spine
ctx.strokeStyle='rgba(50,110,50,0.55)';
ctx.lineWidth=1;
ctx.beginPath();ctx.moveTo(ML,MT);ctx.lineTo(ML,H-MB);ctx.stroke();
// X-axis spine
ctx.beginPath();ctx.moveTo(ML,H-MB);ctx.lineTo(W-MR,H-MB);ctx.stroke();

// Rotated "cycles" label on y-axis
ctx.save();
ctx.translate(10,MT+ph()/2);ctx.rotate(-Math.PI/2);
ctx.fillStyle='rgba(70,140,70,0.6)';
ctx.font='10px monospace';ctx.textAlign='center';
ctx.fillText('cycles',0,0);
ctx.restore();

}

// ─ Bar rendering ─────────────────────────────────────────── function drawBars(pts,count){ var n=pts.length; var bw=pw()/n; // width per bar var bottom=toY(0); for(var i=0;i<count;i++){ var v=pts[i]; // integer cycle count var top=toY(v); var bh=bottom-top; var x=ML+i*bw; // Color by magnitude: dim for baseline, bright+glow for spikes if(v>=20){ ctx.fillStyle='rgba(0,230,90,0.9)'; ctx.shadowColor='#00ff88';ctx.shadowBlur=5; } else if(v>=14){ ctx.fillStyle='rgba(0,210,80,0.7)'; ctx.shadowBlur=0; } else { ctx.fillStyle='rgba(0,180,70,0.45)'; ctx.shadowBlur=0; } ctx.fillRect(x+1,top,Math.max(1,bw-2),bh); ctx.shadowBlur=0; } }

// ─ Full draw states ───────────────────────────────────────── function drawEmpty(){ if(!ctx)return; drawAxes(); ctx.fillStyle='rgba(60,125,60,0.45)'; ctx.font='13px monospace';ctx.textAlign='center'; ctx.fillText('press a key to capture trace',ML+pw()/2,MT+ph()/2+5); ctx.textAlign='left'; }

function drawFrame(pts,progress){ if(!ctx)return; drawAxes(); var count=Math.floor(pts.lengthprogress); drawBars(pts,count); // Scan cursor if(progress<1&&count<pts.length){ var cx=ML+count(pw()/pts.length); ctx.strokeStyle='rgba(0,220,90,0.45)'; ctx.lineWidth=1;ctx.setLineDash([2,3]); ctx.beginPath();ctx.moveTo(cx,MT);ctx.lineTo(cx,canvas.height-MB);ctx.stroke(); ctx.setLineDash([]); } }

// ─ Animation ──────────────────────────────────────────────── function animate(key){ if(animId)cancelAnimationFrame(animId); var rec=document.getElementById('kdRecovered'); rec.textContent='capturing…'; var pts=TRACES[key]; if(!pts){rec.textContent='';return;} var start=null,dur=1600; function frame(ts){ if(!start)start=ts; var p=Math.min((ts-start)/dur,1); p=p<0.5?2pp:1-Math.pow(-2*p+2,2)/2; // ease in-out drawFrame(pts,p); if(p<1){animId=requestAnimationFrame(frame);} else{rec.textContent='key recovered: '+key;} } animId=requestAnimationFrame(frame); }

// ─ Keypad interaction ──────────────────────────────────────── function updateDisplay(){ var s=pin.map(function(){return'●';}); while(s.length<4)s.push('_'); document.getElementById('kdDisplay').textContent=s.join(' '); }

function pressKey(k){ if(k==='C'){ pin=[];updateDisplay(); if(animId)cancelAnimationFrame(animId); drawEmpty(); document.getElementById('kdRecovered').textContent=''; return; } if(k==='OK'){pin=[];updateDisplay();return;} if(pin.length>=4)return; pin.push(k);updateDisplay();animate(k); }

document.querySelectorAll('.kd-key').forEach(function(btn){ btn.addEventListener('click',function(){pressKey(btn.dataset.k);}); });

document.addEventListener('keydown',function(e){ if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA')return; var k=e.key; if('0123456789'.indexOf(k)>=0){ var btn=document.querySelector('.kd-key[data-k="'+k+'"]'); if(btn){btn.classList.add('kd-pressed');setTimeout(function(){btn.classList.remove('kd-pressed');},150);} pressKey(k); }else if(k==='Backspace'||k==='Delete'){pressKey('C');} else if(k==='Enter'){pressKey('OK');} });

// ─ Load traces from JSON, then init ───────────────────────── function init(){ fetch('assets/data/keypad-traces.json') .then(function(r){return r.json();}) .then(function(data){ // Support both {traces:{...}} and flat {"0":[...]} formats TRACES=data.traces||data; drawEmpty(); }) .catch(function(){drawEmpty();}); }

if(document.readyState==='loading'){ document.addEventListener('DOMContentLoaded',init); }else{ init(); } })(); </script>

This vulnerability is tracked as CVE-2025-54764 and has been fixed in Mbed TLS v3.6.5 and Trusted Firmware-M v2.2.2.

Questions and Answers

Who conducted this research?

The research was conducted by a team of four researchers from Universidade do Minho and KU Leuven.

What is a microcontroller, and why does it matter for security?

A microcontroller (MCU) is a tiny, low-cost chip that combines a processor, memory, and peripherals in a single package. MCUs are found in an enormous range of everyday devices: smart home sensors, fitness trackers, medical implants, industrial controllers, automotive systems, payment terminals, and more. Arm estimates that over billions of Cortex-M-based chips are shipped every year.

Unlike the powerful processors in laptops or cloud servers, MCUs are optimized for low power and real-time responsiveness — but they are increasingly trusted with sensitive tasks: storing cryptographic keys, authenticating users, securing firmware boot, and protecting confidential data. This makes them high-value targets for attackers seeking to compromise devices at scale.

What is Arm TrustZone-M?

TrustZone-M is a hardware security feature built into modern Arm Cortex-M microcontrollers (the Armv8-M architecture). It divides the chip into two isolated worlds: a Secure World for trusted, security-sensitive code (e.g., cryptographic key management, secure boot, authentication), and a Non-Secure World for the regular application software.

The key guarantee of TrustZone-M is hardware-enforced isolation: even if the non-secure application software is fully compromised by an attacker, it should not be able to observe or tamper with the code and data inside the Secure World. M-Step breaks this guarantee through a software-only, microarchitectural side-channel attack.

How does single-stepping work, and why is it so powerful?

Processors handle hardware interrupts — signals that briefly pause the running program to handle an urgent event, such as a timer firing. On Cortex-M, the non-secure world controls these timers and can use them to preempt secure-world execution.

By programming a timer interrupt to fire after every single instruction, M-Step forces the victim program to execute just one instruction before being paused. The attacker then measures the interrupt latency — the precise time from when the interrupt fires to when control is returned — which varies depending on what instruction the secure code executed and what data it accessed.

This instruction-granular view is extremely powerful: secret-dependent branches or data accesses that are invisible to a coarser observer leave a measurable timing signature. The same principle powers the SGX-Step framework for Intel SGX, which has enabled more than 48 published security attacks.

M-Step leakage heatmaps: data-dependent division timings, instruction differentiation, cache covert channel, and memory contention

M-Step leakage analysis (Figure 6 from the paper) — each heatmap shows interrupt latency or memory-access probability measured on the real Cortex-M33 target. Left to right: (a) data-dependent integer division timings leak both divisor and dividend values; (b) eight representative instructions produce clearly distinct latency profiles; (c) an ICache covert channel maps each instruction to the cache line it accesses; (d) memory contention distinguishes SRAM1, SRAM2, Flash, and peripheral bus accesses. Together these show that M-Step captures a wide range of microarchitectural leakage sources far beyond simple interrupt latency.

Wasn't TrustZone-M supposed to be immune to these attacks?

Yes. The prior work Nemesis (ACM CCS '18) studied interrupt-latency attacks on microcontrollers and concluded that Cortex-M platforms were "immune" to such attacks. The reasoning was that the simpler MCU architecture, combined with Cortex-M's interrupt-response optimizations, prevented reliable instruction-level observation.

M-Step shows this conclusion was premature. By profiling Cortex-M interrupt handling at the instruction level — uncovering the three instruction classes (atomic, resumable, and restartable) described above — we developed an adaptive algorithm that dynamically adjusts the interrupt timer for each instruction type. The result is reliable, deterministic single-stepping of arbitrary production code on real Cortex-M33 hardware, debunking the prior immunity claim.

What is Mbed TLS, and who uses it?

Mbed TLS is an open-source, lightweight cryptographic library developed by Arm, designed specifically for embedded and IoT devices. It implements standard cryptographic algorithms (RSA, AES, ECDSA, etc.) and is widely used across the embedded industry.

Crucially, Mbed TLS is the cryptographic backbone of Trusted Firmware-M (TF-M), Arm's official open-source reference implementation for TrustZone-M. This means Mbed TLS runs directly inside the Secure World on a broad range of production TrustZone-M devices from manufacturers including ST Microelectronics, NXP, Nordic Semiconductor, and others.

What can an attacker actually steal?

Using M-Step against a device running vulnerable Mbed TLS, an attacker who has compromised the non-secure OS can recover RSA private keys stored inside the Secure World — the long-term cryptographic secret used to authenticate a device, sign firmware updates, or decrypt sensitive communications.

In our demonstration, full key recovery succeeds from a single execution trace, with no need for repeated measurements. In practice, this means a software exploit on a connected MCU-based device can silently steal the cryptographic keys that TrustZone-M was designed to protect, potentially enabling device impersonation, firmware forgery, or decryption of past and future communications.

How realistic is this attack?

M-Step requires software control of the non-secure OS on the target device — an attacker who has already exploited a vulnerability in the application software. This is a realistic scenario for Internet-connected IoT devices, which are well known to run software with vulnerabilities and may be reachable by remote attackers.

Importantly, M-Step is a fully software-based attack: no physical access, specialized hardware, oscilloscopes, or debug connectors are required. The entire attack runs from the compromised non-secure software side of the chip, making it stealthy and practically relevant even for devices deployed in the field.

Once non-secure OS control is achieved, M-Step's key extraction is fast and reliable — a single execution trace suffices.

Has this been fixed?

Yes — the immediate vulnerability in Mbed TLS has been patched following responsible disclosure to Arm, Trusted Firmware-M, and Mbed TLS on June 28, 2025:

  • Mbed TLS v3.6.5 (October 15, 2025): replaced the vulnerable BEEA implementation with a constant-time alternative.
  • Trusted Firmware-M v2.2.2 (November 27, 2025): incorporated the patched Mbed TLS.
  • Arm assigned CVE-2025-54764 and published a security advisory.
  • ST Microelectronics published guidance pointing users to the updated Mbed TLS version.

However, the underlying attack surface — interrupt-driven microarchitectural side channels on TrustZone-M — is not closed by a single patch. Other non-constant-time code running in the Secure World may still be observable via M-Step. Developers of TrustZone-M applications should audit their security-critical code for timing leakage.

My device uses a hardware cryptographic accelerator — am I safe?

Not necessarily. Many MCUs include dedicated hardware crypto engines (such as the ST SAES and SPKA on STM32L5), which are often assumed to eliminate software-level side-channel risks. Our experiments show that 7 of the 24 vulnerable Mbed TLS call sites remain exploitable even on ST platforms with hardware accelerators, because software fallback code in Mbed TLS still executes the vulnerable BEEA algorithm on certain code paths.

Regardless of whether a device uses hardware crypto acceleration, updating to Mbed TLS v3.6.5 and TF-M v2.2.2 is required to mitigate CVE-2025-54764.

What are the main takeaways?

  1. TrustZone-M is not immune to microarchitectural side-channel attacks.

    Prior assumptions about the safety of Cortex-M platforms must be revised. Interrupt-driven single-stepping is practical on modern MCUs, opening the door to the same class of high-impact attacks previously demonstrated on high-end processors.

  2. Constant-time programming matters on microcontrollers too.

    The Mbed TLS BEEA vulnerability illustrates that non-constant-time algorithms can expose cryptographic secrets even on the simplest platforms, especially when a high-resolution observation primitive like M-Step is available.

  3. Hardware accelerators are not a complete defense.

    Devices with dedicated hardware crypto engines may still be vulnerable through software fallback paths. End-to-end constant-time guarantees must be verified for all code paths, including fallbacks.

  4. Update affected software.

    Devices running older versions of Mbed TLS or TF-M should update to Mbed TLS v3.6.5 and TF-M v2.2.2 to mitigate the specific vulnerability (CVE-2025-54764) we exploited.

Is the M-Step code available?

Yes. The complete M-Step framework — including the single-stepping core, microarchitectural side-channel plugins (interrupt latency, cache activity, bus contention), and proof-of-concept attack code — is open-source and available on GitHub. The repository includes all code needed to reproduce our results on an STM32L5 development board (Arm Cortex-M33).

The USENIX Security '26 artifact evaluation awarded M-Step all three badges: Available, Functional, and Reproduced.

What is the M-Step logo, and where does it come from?

Where can I learn more?

Recognition & Resources

USENIX Security '26 AE: Available AE: Functional AE: Reproduced ★ Black Hat USA '26 — Accepted