Assumed-Immutable Data is Stored in Writable Memory

Description

Assumed-Immutable Data is Stored in Writable Memory occurs when security-critical assets like bootloaders, device identifiers, and configuration settings that should remain unchangeable are instead stored in memory that can be reprogrammed or updated. Trusted system components including initial bootloaders, cryptographic keys, and hash digests require immutability to establish a secure foundation. Storing these in read-only memory (ROM), fuses, or one-time programmable (OTP) memory provides integrity guarantees. When such assets end up in writable memory, the root of trust is compromised.

Risk

Mutable immutable data has severe security implications. Root of trust is compromised. Bootloaders can be replaced. Cryptographic keys can be modified. Hash digests can be falsified. Device identity can be forged. Security policies can be changed. Firmware verification can be bypassed. Entire security model can collapse.

Solution

All immutable code and data should be programmed into ROM or write-once memory rather than writable storage. Use hardware fuses for critical security settings. Implement one-time programmable (OTP) memory for keys and certificates. Protect critical data with memory protection units. Verify data integrity against secure reference values.

Common Consequences

ImpactDetails
IntegrityScope: Integrity

Modify Memory - Assumed-immutable data can be changed.
Access ControlScope: Access Control

Bypass Protection Mechanism - Security verification can be circumvented.
AuthenticationScope: Authentication

Gain Privileges - Device identity can be forged.

Example Code

Vulnerable Code

// Vulnerable: Security-critical data in writable memory

module vulnerable_boot_storage (
    input wire clk,
    input wire reset_n,
    input wire [15:0] addr,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire read_enable,
    output reg [31:0] read_data
);

    // VULNERABLE: Security-critical data stored in writable RAM
    reg [31:0] memory [0:65535];

    // Security-critical addresses (should be immutable)
    parameter BOOTLOADER_START = 16'h0000;
    parameter BOOTLOADER_END = 16'h0FFF;
    parameter GOLDEN_HASH_ADDR = 16'h1000;
    parameter ROOT_KEY_ADDR = 16'h1010;
    parameter DEVICE_ID_ADDR = 16'h1020;

    // Initialize with security-critical data
    initial begin
        // VULNERABLE: These should be in ROM/OTP, not RAM
        memory[GOLDEN_HASH_ADDR] = 32'hA5B6C7D8;     // Hash for verification
        memory[ROOT_KEY_ADDR] = 32'h12345678;        // Root public key
        memory[DEVICE_ID_ADDR] = 32'hDEVICE01;       // Device identifier
    end

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            read_data <= 32'h0;
        end
        else begin
            if (read_enable) begin
                read_data <= memory[addr];
            end

            // VULNERABLE: Write to ANY address including security-critical
            if (write_enable) begin
                memory[addr] <= write_data;

                // Attacker can:
                // 1. Overwrite GOLDEN_HASH_ADDR with hash of malicious code
                // 2. Replace ROOT_KEY_ADDR with attacker's key
                // 3. Forge DEVICE_ID_ADDR to impersonate another device
            end
        end
    end

endmodule

// Vulnerable: Bootloader in writable flash
module vulnerable_bootloader_storage (
    input wire clk,
    input wire reset_n,
    input wire [15:0] flash_addr,
    input wire [31:0] flash_write_data,
    input wire flash_write,
    input wire flash_read,
    output reg [31:0] flash_read_data,
    // Flash control
    input wire flash_erase,
    input wire [7:0] sector_select
);

    // VULNERABLE: Bootloader stored in reprogrammable flash
    reg [31:0] flash_memory [0:65535];

    // Bootloader in first 4KB (0x0000-0x0FFF)
    // VULNERABLE: Can be erased and reprogrammed

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            flash_read_data <= 32'h0;
        end
        else begin
            if (flash_read) begin
                flash_read_data <= flash_memory[flash_addr];
            end

            // VULNERABLE: No protection for bootloader region
            if (flash_write) begin
                flash_memory[flash_addr] <= flash_write_data;
            end

            // VULNERABLE: Bootloader sector can be erased
            if (flash_erase && sector_select == 8'h00) begin
                // Erase bootloader sector - catastrophic!
                // Attacker can then program malicious bootloader
            end
        end
    end

endmodule
// Vulnerable: Software with mutable security data

#include <stdint.h>

// VULNERABLE: Security-critical data in writable memory

// In .data section - writable
static uint8_t golden_hash[32] = {
    0xDE, 0xAD, 0xBE, 0xEF, /* ... */
};

// In .data section - writable
static uint8_t root_public_key[64] = {
    0x04, /* ... */
};

// In .bss section - writable
static uint32_t device_id;

// VULNERABLE: Hash verification using mutable reference
bool vulnerable_verify_firmware(const uint8_t* firmware, size_t len) {
    uint8_t computed_hash[32];

    sha256(firmware, len, computed_hash);

    // VULNERABLE: Comparing against mutable golden_hash
    // Attacker can modify golden_hash to match malicious firmware
    return memcmp(computed_hash, golden_hash, 32) == 0;
}

// VULNERABLE: Initialize device ID from writable memory
void vulnerable_init_device_id(void) {
    // VULNERABLE: Device ID stored in writable flash
    // Can be modified to impersonate another device

    uint32_t* flash_device_id = (uint32_t*)0x08000000;
    device_id = *flash_device_id;

    // Attacker can reprogram flash with different device ID
}

// VULNERABLE: Certificate chain using mutable root
bool vulnerable_verify_certificate(const uint8_t* cert, size_t len) {
    // VULNERABLE: Root key can be replaced by attacker
    return verify_signature(cert, len, root_public_key);
}

Fixed Code

// Fixed: Security-critical data in immutable storage

module secure_boot_storage (
    input wire clk,
    input wire reset_n,
    input wire [15:0] addr,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire read_enable,
    output reg [31:0] read_data,
    output reg write_denied
);

    // Regular RAM for non-critical data
    reg [31:0] ram [0:32767];

    // FIXED: Security-critical data in ROM (synthesized as hardwired logic)
    wire [31:0] rom_data [0:255];

    // ROM contents - cannot be modified
    assign rom_data[0] = 32'hA5B6C7D8;   // Golden hash
    assign rom_data[1] = 32'h12345678;   // Root key word 0
    assign rom_data[2] = 32'h9ABCDEF0;   // Root key word 1
    // ... more ROM data

    // FIXED: OTP fuse data for device-specific immutable data
    wire [127:0] otp_device_id;
    wire [255:0] otp_device_key;

    otp_fuse_block otp (
        .clk(clk),
        .device_id(otp_device_id),
        .device_key(otp_device_key)
    );

    // Address decode
    wire is_rom_access = (addr >= 16'h0000) && (addr < 16'h0100);
    wire is_otp_access = (addr >= 16'h0100) && (addr < 16'h0110);
    wire is_ram_access = (addr >= 16'h8000);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            read_data <= 32'h0;
            write_denied <= 1'b0;
        end
        else begin
            write_denied <= 1'b0;

            if (read_enable) begin
                if (is_rom_access) begin
                    // FIXED: Read from ROM
                    read_data <= rom_data[addr[7:0]];
                end
                else if (is_otp_access) begin
                    // FIXED: Read from OTP
                    read_data <= otp_device_id[addr[3:0] * 32 +: 32];
                end
                else if (is_ram_access) begin
                    read_data <= ram[addr - 16'h8000];
                end
            end

            if (write_enable) begin
                if (is_rom_access) begin
                    // FIXED: ROM writes denied
                    write_denied <= 1'b1;
                end
                else if (is_otp_access) begin
                    // FIXED: OTP writes denied (can only be programmed once)
                    write_denied <= 1'b1;
                end
                else if (is_ram_access) begin
                    // RAM writes allowed
                    ram[addr - 16'h8000] <= write_data;
                end
            end
        end
    end

endmodule

// Fixed: Protected bootloader with write-once storage
module secure_bootloader_storage (
    input wire clk,
    input wire reset_n,
    input wire [15:0] addr,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire read_enable,
    input wire [3:0] requester_id,
    output reg [31:0] read_data,
    output reg access_denied,
    // Boot status
    input wire boot_complete,
    output reg protection_active
);

    // Bootloader in protected ROM region
    wire [31:0] bootloader_rom [0:4095];

    // Application flash (writable by authorized users)
    reg [31:0] app_flash [0:61439];

    // FIXED: Protection lock - cannot be cleared once set
    reg bootloader_locked;

    // Bootloader region
    parameter BOOTLOADER_START = 16'h0000;
    parameter BOOTLOADER_END = 16'h0FFF;

    // FIXED: Trusted requester only
    parameter SECURE_MASTER = 4'd0;

    wire is_bootloader_access = (addr >= BOOTLOADER_START) &&
                                (addr <= BOOTLOADER_END);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            read_data <= 32'h0;
            access_denied <= 1'b0;
            bootloader_locked <= 1'b0;
            protection_active <= 1'b0;
        end
        else begin
            access_denied <= 1'b0;

            // FIXED: Lock bootloader after boot completes
            if (boot_complete && !bootloader_locked) begin
                bootloader_locked <= 1'b1;
                protection_active <= 1'b1;
            end

            if (read_enable) begin
                if (is_bootloader_access) begin
                    // Bootloader reads always allowed
                    read_data <= bootloader_rom[addr];
                end
                else begin
                    read_data <= app_flash[addr - 16'h1000];
                end
            end

            if (write_enable) begin
                if (is_bootloader_access) begin
                    // FIXED: Bootloader writes always denied (ROM)
                    access_denied <= 1'b1;
                end
                else if (bootloader_locked && requester_id != SECURE_MASTER) begin
                    // App flash restricted after boot
                    access_denied <= 1'b1;
                end
                else begin
                    app_flash[addr - 16'h1000] <= write_data;
                end
            end
        end
    end

endmodule
// Fixed: Software with immutable security data

#include <stdint.h>
#include <stdbool.h>

// FIXED: Security-critical data in read-only section
// Compiler places this in .rodata (typically in flash/ROM)
static const uint8_t golden_hash[32] __attribute__((section(".rodata.security"))) = {
    0xDE, 0xAD, 0xBE, 0xEF, /* ... */
};

// FIXED: Root public key in read-only memory
static const uint8_t root_public_key[64] __attribute__((section(".rodata.security"))) = {
    0x04, /* ... */
};

// FIXED: Read device ID from OTP/fuse
static uint32_t read_device_id_from_otp(void) {
    // OTP register is read-only, cannot be modified after programming
    volatile uint32_t* otp_device_id = (volatile uint32_t*)OTP_DEVICE_ID_ADDR;
    return *otp_device_id;
}

// FIXED: Verify golden hash is in ROM
static bool verify_golden_hash_integrity(void) {
    // Check that golden_hash is in expected ROM region
    uintptr_t addr = (uintptr_t)golden_hash;
    if (addr < ROM_START || addr >= ROM_END) {
        // Golden hash not in ROM - security violation!
        return false;
    }

    // Verify MPU is protecting this region
    if (!mpu_region_is_read_only(addr)) {
        return false;
    }

    return true;
}

// FIXED: Hash verification using immutable reference
bool secure_verify_firmware(const uint8_t* firmware, size_t len) {
    // FIXED: Verify golden hash is protected
    if (!verify_golden_hash_integrity()) {
        log_security_error("Golden hash integrity check failed");
        return false;
    }

    uint8_t computed_hash[32];
    sha256(firmware, len, computed_hash);

    // Compare against immutable golden_hash in ROM
    return secure_compare(computed_hash, golden_hash, 32);
}

// FIXED: Use MPU to protect security-critical data
void protect_security_data(void) {
    // Configure MPU region for security data as read-only

    mpu_region_config_t security_region = {
        .base_address = (uint32_t)&golden_hash,
        .size = 4096,  // Protect entire security section
        .attributes = MPU_REGION_RO | MPU_REGION_EXEC_NEVER,
        .enable = true
    };

    configure_mpu_region(MPU_SECURITY_REGION, &security_region);

    // FIXED: Lock MPU configuration
    lock_mpu_configuration();
}

// FIXED: Get device ID from immutable source
uint32_t get_device_id(void) {
    static uint32_t cached_device_id = 0;
    static bool device_id_cached = false;

    if (!device_id_cached) {
        // Read from OTP (immutable)
        cached_device_id = read_device_id_from_otp();
        device_id_cached = true;
    }

    return cached_device_id;
}

// FIXED: Linker script excerpt to place security data in ROM
/*
SECTIONS {
    .rodata.security : {
        __security_data_start = .;
        KEEP(*(.rodata.security))
        __security_data_end = .;
    } > ROM

    ASSERT(__security_data_end <= ROM_END,
           "Security data must fit in ROM");
}
*/

CVE Examples

Mutable security data vulnerabilities have been found in various embedded systems where hash digests or cryptographic keys were stored in writable flash, allowing attackers to modify verification data and bypass security checks.


  • CWE-668: Exposure of Resource to Wrong Sphere (parent)
  • CWE-471: Modification of Assumed-Immutable Data (can precede)
  • CWE-1202: Memory and Storage Issues (category)
  • CAPEC-458: Flash Memory Attacks (attack pattern)
  • CAPEC-679: Exploitation of Improperly Configured Memory Protections (attack pattern)

References

  1. MITRE Corporation. "CWE-1282: Assumed-Immutable Data is Stored in Writable Memory." https://cwe.mitre.org/data/definitions/1282.html
  2. ARM. "TrustZone Memory Protection"
  3. NIST. "Root of Trust Guidelines"