Mutable Attestation or Measurement Reporting Data
Description
Mutable Attestation or Measurement Reporting Data occurs when attestation or measurement registers used in secure boot processes can be modified by adversaries, allowing them to spoof boot flow integrity measurements. During SoC secure/verified boot, systems calculate cryptographic hashes of code binaries and extend them sequentially. These final hash values—stored in registers for later attestation—can be read to detect tampering. The weakness occurs when these registers lack proper protections, enabling attackers to write arbitrary hash values and falsify boot integrity claims.
Risk
Mutable attestation data has severe security implications. Boot integrity can be spoofed. Measurement chains can be falsified. Attestation reports become unreliable. Malicious boot goes undetected. Trust verification is bypassed. Remote attestation is compromised. TPM-like guarantees are lost. Secure boot can be defeated.
Solution
Store measurement data in registers with read-only access protections. Ensure immutability against direct or indirect modification. Implement proper access controls preventing untrusted agent modification. Use extend-only operations for measurements. Lock measurement registers after boot completes. Verify measurement register protection in security audits.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Application Data - False attestation enables unauthorized access. |
| Integrity | Scope: Integrity Modify Memory - Measurement data can be falsified. |
| Access Control | Scope: Access Control Bypass Protection Mechanism - Attestation verification is bypassed. |
Example Code
Vulnerable Code
// Vulnerable: Measurement registers without write protection
module vulnerable_measurement_engine (
input wire clk,
input wire reset_n,
input wire [255:0] hash_input,
input wire extend_command,
input wire [7:0] pcr_select,
input wire [255:0] direct_write_data,
input wire direct_write_enable,
input wire [3:0] requester_id,
output reg [255:0] pcr_read_data,
output reg measurement_done
);
// Platform Configuration Registers (PCRs)
reg [255:0] pcr [0:23];
// VULNERABLE: PCRs can be directly written
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
// Initialize PCRs to zero
integer i;
for (i = 0; i < 24; i = i + 1) begin
pcr[i] <= 256'h0;
end
measurement_done <= 1'b0;
end
else begin
measurement_done <= 1'b0;
if (extend_command) begin
// Normal extend operation: PCR = Hash(PCR || new_data)
pcr[pcr_select] <= sha256({pcr[pcr_select], hash_input});
measurement_done <= 1'b1;
end
// VULNERABLE: Direct write to PCRs allowed
if (direct_write_enable) begin
// Any requester can write arbitrary values!
pcr[pcr_select] <= direct_write_data;
// Attacker can:
// 1. Set PCR to expected "good" value
// 2. Hide malicious boot components
// 3. Pass attestation with compromised system
end
end
end
// Read PCR value
always @(*) begin
pcr_read_data = pcr[pcr_select];
end
endmodule
// Vulnerable: Attestation report from mutable source
module vulnerable_attestation (
input wire clk,
input wire reset_n,
input wire generate_report,
input wire [7:0] pcr_mask,
output reg [2047:0] attestation_report,
output reg report_valid
);
// VULNERABLE: Measurement registers can be modified
reg [255:0] measurements [0:7];
// VULNERABLE: Anyone can update measurements
input wire [255:0] measurement_update;
input wire [2:0] measurement_select;
input wire update_measurement;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
report_valid <= 1'b0;
end
else begin
// VULNERABLE: No access control on measurement updates
if (update_measurement) begin
measurements[measurement_select] <= measurement_update;
end
if (generate_report) begin
// Generate report from mutable measurements
// Report is untrustworthy!
attestation_report <= {measurements[0], measurements[1],
measurements[2], measurements[3],
measurements[4], measurements[5],
measurements[6], measurements[7]};
report_valid <= 1'b1;
end
end
end
endmodule
// Vulnerable: Software with mutable measurement storage
#include <stdint.h>
// VULNERABLE: Measurements stored in writable memory
static uint8_t pcr_values[24][32]; // In .bss - writable
// VULNERABLE: Direct PCR write function
void vulnerable_pcr_write(uint8_t pcr_index, const uint8_t* value) {
// VULNERABLE: No access control
// Any code can write arbitrary values to PCRs
if (pcr_index < 24) {
memcpy(pcr_values[pcr_index], value, 32);
}
}
// VULNERABLE: Extend function that can be bypassed
void vulnerable_pcr_extend(uint8_t pcr_index, const uint8_t* data, size_t len) {
if (pcr_index >= 24) return;
// Normal extend: new_value = SHA256(old_value || data)
uint8_t buffer[64];
memcpy(buffer, pcr_values[pcr_index], 32);
memcpy(buffer + 32, data, len > 32 ? 32 : len);
sha256(buffer, 64, pcr_values[pcr_index]);
// Problem: Attacker can call vulnerable_pcr_write() directly
// to set PCR to any value, bypassing the extend chain
}
// VULNERABLE: Attestation using mutable PCRs
bool vulnerable_generate_attestation(uint8_t* report, size_t* report_len) {
// Quote PCR values (which may have been tampered)
for (int i = 0; i < 24; i++) {
memcpy(report + i * 32, pcr_values[i], 32);
}
// Sign with attestation key
sign_report(report, 24 * 32);
*report_len = 24 * 32 + 64; // PCRs + signature
return true;
// Report is signed but PCR values are untrustworthy
}
Fixed Code
// Fixed: Measurement registers with write protection
module secure_measurement_engine (
input wire clk,
input wire reset_n,
input wire [255:0] hash_input,
input wire extend_command,
input wire [7:0] pcr_select,
input wire [3:0] requester_id,
input wire boot_complete,
output reg [255:0] pcr_read_data,
output reg measurement_done,
output reg access_denied,
output reg pcrs_locked
);
// Platform Configuration Registers (PCRs)
reg [255:0] pcr [0:23];
// FIXED: PCR lock status - once locked, cannot be modified
reg [23:0] pcr_lock;
// FIXED: Trusted requesters for different PCRs
parameter SECURE_BOOT_ROM = 4'd0;
parameter SECURE_BOOT_LOADER = 4'd1;
parameter OS_KERNEL = 4'd2;
// PCR assignments
// PCR 0-3: Boot ROM measurements (ROM only)
// PCR 4-7: Bootloader measurements (Bootloader only)
// PCR 8-15: OS measurements (Kernel only)
// PCR 16-23: Application measurements (various)
function automatic is_pcr_writable;
input [7:0] pcr_idx;
input [3:0] requester;
begin
is_pcr_writable = 1'b0;
// Check PCR range and requester
if (pcr_idx < 4 && requester == SECURE_BOOT_ROM) begin
is_pcr_writable = 1'b1;
end
else if (pcr_idx >= 4 && pcr_idx < 8 && requester == SECURE_BOOT_LOADER) begin
is_pcr_writable = 1'b1;
end
else if (pcr_idx >= 8 && pcr_idx < 16 && requester == OS_KERNEL) begin
is_pcr_writable = 1'b1;
end
// PCR 16-23 have more flexible policies
end
endfunction
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
integer i;
for (i = 0; i < 24; i = i + 1) begin
pcr[i] <= 256'h0;
end
pcr_lock <= 24'h0;
measurement_done <= 1'b0;
access_denied <= 1'b0;
pcrs_locked <= 1'b0;
end
else begin
measurement_done <= 1'b0;
access_denied <= 1'b0;
// FIXED: Lock PCRs after boot
if (boot_complete) begin
pcr_lock <= 24'hFFFFFF; // Lock all PCRs
pcrs_locked <= 1'b1;
end
if (extend_command) begin
// FIXED: Check if PCR is locked
if (pcr_lock[pcr_select]) begin
access_denied <= 1'b1;
end
// FIXED: Check requester authorization
else if (!is_pcr_writable(pcr_select, requester_id)) begin
access_denied <= 1'b1;
end
else begin
// FIXED: Only extend operation - no direct write
pcr[pcr_select] <= sha256({pcr[pcr_select], hash_input});
measurement_done <= 1'b1;
end
end
// FIXED: No direct write interface
// PCRs can only be modified through extend operation
end
end
// Read PCR value (always allowed)
always @(*) begin
pcr_read_data = pcr[pcr_select];
end
endmodule
// Fixed: Secure attestation with protected measurements
module secure_attestation (
input wire clk,
input wire reset_n,
input wire generate_report,
input wire [23:0] pcr_mask,
input wire [255:0] nonce,
input wire [3:0] requester_id,
output reg [2047:0] attestation_report,
output reg report_valid,
output reg report_signed
);
// FIXED: Measurements from protected PCR engine
wire [255:0] pcr_values [0:23];
wire pcrs_locked;
secure_measurement_engine pcr_engine (
// ... connections ...
.pcrs_locked(pcrs_locked)
);
// FIXED: Attestation key in hardware
wire [255:0] attestation_private_key; // From secure key storage
// State machine for attestation
reg [2:0] attest_state;
parameter IDLE = 3'd0;
parameter COLLECT = 3'd1;
parameter HASH = 3'd2;
parameter SIGN = 3'd3;
parameter DONE = 3'd4;
reg [7:0] pcr_index;
reg [2047:0] report_buffer;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
report_valid <= 1'b0;
report_signed <= 1'b0;
attest_state <= IDLE;
end
else begin
case (attest_state)
IDLE: begin
report_valid <= 1'b0;
report_signed <= 1'b0;
if (generate_report) begin
// FIXED: Only generate report if PCRs are locked
if (pcrs_locked) begin
attest_state <= COLLECT;
pcr_index <= 8'h0;
report_buffer <= {nonce, 1792'h0}; // Include nonce
end
end
end
COLLECT: begin
// FIXED: Collect PCR values from protected engine
if (pcr_mask[pcr_index]) begin
// Include this PCR in report
report_buffer <= {report_buffer, pcr_values[pcr_index]};
end
pcr_index <= pcr_index + 1;
if (pcr_index >= 23) begin
attest_state <= HASH;
end
end
HASH: begin
// Hash the report for signing
attest_state <= SIGN;
end
SIGN: begin
// FIXED: Sign with protected attestation key
// Signature proves report came from this device
attestation_report <= sign_ecdsa(report_buffer,
attestation_private_key);
report_signed <= 1'b1;
attest_state <= DONE;
end
DONE: begin
report_valid <= 1'b1;
attest_state <= IDLE;
end
endcase
end
end
endmodule
// Fixed: Software with protected measurement storage
#include <stdint.h>
#include <stdbool.h>
// FIXED: PCR operations go through hardware TPM-like module
#define TPM_PCR_EXTEND_REG 0x50001000
#define TPM_PCR_READ_REG 0x50001004
#define TPM_PCR_SELECT_REG 0x50001008
#define TPM_STATUS_REG 0x5000100C
#define TPM_PCR_LOCKED_BIT 0x01
// FIXED: PCRs are in hardware, software only has extend interface
static bool pcr_extend(uint8_t pcr_index, const uint8_t* data, size_t len) {
volatile uint32_t* pcr_select = (volatile uint32_t*)TPM_PCR_SELECT_REG;
volatile uint32_t* status = (volatile uint32_t*)TPM_STATUS_REG;
volatile uint32_t* extend_reg = (volatile uint32_t*)TPM_PCR_EXTEND_REG;
if (pcr_index >= 24) {
return false;
}
// Check if PCRs are locked
if (*status & TPM_PCR_LOCKED_BIT) {
log_error("PCRs are locked");
return false;
}
// Select PCR
*pcr_select = pcr_index;
// FIXED: Only extend operation available - no direct write
// Hardware performs: PCR = SHA256(PCR || data)
for (size_t i = 0; i < len; i += 4) {
uint32_t word;
memcpy(&word, data + i, 4);
*extend_reg = word;
}
// Wait for operation to complete
while (!(*status & TPM_EXTEND_COMPLETE_BIT)) {
// Wait
}
return true;
}
// FIXED: Read PCR from hardware
static bool pcr_read(uint8_t pcr_index, uint8_t* value) {
volatile uint32_t* pcr_select = (volatile uint32_t*)TPM_PCR_SELECT_REG;
volatile uint32_t* read_reg = (volatile uint32_t*)TPM_PCR_READ_REG;
if (pcr_index >= 24) {
return false;
}
*pcr_select = pcr_index;
// Read 32 bytes (256 bits)
for (int i = 0; i < 8; i++) {
uint32_t word = read_reg[i];
memcpy(value + i * 4, &word, 4);
}
return true;
}
// FIXED: No direct PCR write function
// void pcr_write(...) - DOES NOT EXIST
// FIXED: Attestation using hardware-protected PCRs
bool secure_generate_attestation(const uint8_t* nonce, size_t nonce_len,
uint8_t* report, size_t* report_len) {
attestation_request_t req;
attestation_response_t resp;
// Build request with nonce (for freshness)
memcpy(req.nonce, nonce, nonce_len);
req.pcr_mask = 0x00FFFFFF; // All 24 PCRs
// FIXED: Request attestation from hardware TPM
// Hardware reads PCRs and signs with protected key
if (!tpm_generate_quote(&req, &resp)) {
return false;
}
// FIXED: Verify signature came from this TPM
if (!verify_quote_signature(&resp)) {
log_error("Quote signature verification failed");
return false;
}
memcpy(report, resp.quote, resp.quote_len);
*report_len = resp.quote_len;
return true;
}
// FIXED: Lock PCRs after boot
void finalize_measurements(void) {
volatile uint32_t* tpm_ctrl = (volatile uint32_t*)TPM_CONTROL_REG;
// Lock all PCRs - cannot be unlocked without reset
*tpm_ctrl |= TPM_LOCK_PCRS;
// Verify lock
volatile uint32_t* status = (volatile uint32_t*)TPM_STATUS_REG;
if (!(*status & TPM_PCR_LOCKED_BIT)) {
log_error("Failed to lock PCRs");
secure_halt();
}
}
CVE Examples
Mutable attestation vulnerabilities have been found in TPM-like implementations where PCR registers could be directly written instead of only extended, allowing attackers to forge attestation reports.
Related CWEs
- CWE-284: Improper Access Control (parent)
- CWE-1196: Security Flow Issues (category)
- CWE-1282: Assumed-Immutable Data is Stored in Writable Memory (related)
- CAPEC-680: Exploitation of Improperly Controlled Registers (attack pattern)
References
- MITRE Corporation. "CWE-1283: Mutable Attestation or Measurement Reporting Data." https://cwe.mitre.org/data/definitions/1283.html
- TCG. "TPM 2.0 Library Specification"
- Intel. "PCIe Device Measurement Requirements"