Footer
General•November 09, 2026

The Secret Network Research Path

MAA Said Debuggable. The Enclave Said OK.

By Rahul Saxena (@saxenism)

Severity: Critical — requires successful registration of a DEBUG re-sign with the production MRENCLAVE

Affected component: Secret3dev/SecretNetwork at commit 2b8e18788e70d79245343ed53bc2cc42a345d2d4 (v1.26.0-community-continuance), along with the September 2026 secret-4 endpoint snapshot reporting 1.26.0 at build commit 4aebd17c3b11c574d7dad07056ee6bd30b48e140 and the September 2026 public v1.27 source snapshot at 91145f355c45fd2464ec486fc800a986cd3b5e2b.

Fix: v1.27.2, released 2026-09-23 and activated on secret-4 at height 27,286,266 (2026-09-23 12:29:45 UTC) under governance proposal 377; v1.27.3 later retained the enclave. Maintainers stated that the v1.27 line rejects DEBUG quotes before releasing seeds and changed node authorization; the public v1.27.2 source tag corroborates the DEBUG rejection.

Secret Network used genuine DCAP quote verification and Azure MAA token verification, then authorized the recipient enclave by comparing its MRENCLAVE with the local enclave's measurement. SGX records DEBUG in a separate attribute: enabling DEBUG does not change MRENCLAVE, but it gives the launching host architectural access to enclave memory.

The shipped enclave could therefore be re-signed under an attacker-controlled key with DEBUG permitted while retaining the accepted production measurement. Azure produced a genuine quote for that enclave, and MAA issued a correctly signed token that explicitly reported is-debuggable=true. The vulnerable code verified the token and its binding to the quote but did not use that claim in its authorization decision.

Once the later registration controls also passed, the receiver encrypted every retained consensus seed to the registration public key carried in the quote. The DEBUG host recovered the corresponding private key from enclave memory and decrypted the vector outside SGX.

Exploit chain

  1. Re-sign the shipped enclave with DEBUG permitted. Re-signing the exact released enclave content changed its signer and SGX attribute mask but not its measured pages. The artifact retained the production MRENCLAVE while allowing a DEBUG launch.
  2. Obtain genuine attestation. On Azure SGX, the enclave produced a DCAP quote with flags 0x7 (INIT | DEBUG | MODE64BIT) and its registration X25519 public key in report_data[0..32]. The West Europe MAA provider returned a valid RS256 token, bound to that quote, with is-debuggable=true.
  3. Submit registration. The transaction-level entry check required a validly signed message and a nonempty, parseable certificate. That was not the complete release gate: trusted processing later verified the quote and token, applied machine-admission rules, and required the exact certificate bytes to appear in verified current-block data.
  4. Receive the encrypted vector. The trusted verifier accepted the genuine attestation and matching MRENCLAVE without rejecting the DEBUG attribute. The registration ECALL then encrypted each retained 32-byte seed to the quoted key. Authenticated encryption produced one 48-byte ciphertext chunk per seed.
  5. Recover the synthetic seeds. The launching host used SGX debug access to recover the registration private key, decrypted each 48-byte chunk, and obtained the corresponding 32-byte synthetic seed. The newest seed reproduced the receiver's current transaction I/O key and state-encryption derivation.

Root cause

The trusted policy compared MRENCLAVE, not DEBUG

The decisive identity check compared only the enclave measurement after quote verification.

Vulnerable-source excerpt from registration/onchain.rs:44-74 (upstream source with line breaks preserved; // ... marks one omitted error! logging call, and the block contains no Bluethroat commentary):

let res = match verify_quote_sgx(attestation, tm_s, true) {
    Ok(res) => {
        trace!("Remote quote verified ok");
        if res.qv_result != sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK {
            trace!("WARNING: {}", res.qv_result);
        }
        res
    }
    Err(e) => {
        trace!("Remote quote verification failed: {}", e);
        return Err(NodeAuthResult::InvalidCert);
    }
};

if (res.body.mr_enclave.m) != SELF_REPORT_BODY.mr_enclave.m {
    // ...
    return Err(NodeAuthResult::MrEnclaveMismatch);
}

Ok(res)

res.body.attributes.flags was not tested here. Because the DEBUG bit is outside MRENCLAVE, a DEBUG re-sign of unchanged enclave content still satisfied this comparison.

MAA reported the missing property, but policy discarded it

The adjacent path verified the MAA JWT signature, bound its signed quote hash to the submitted quote, processed quote-verification status and collateral, and returned the verified report body. It did not consume the token's is-debuggable claim. The relevant paths are registration/attestation.rs:773-900 and shared/crypto/src/dcap.rs:27-113.

A genuine quote proves what the platform reported. It does not make every reported enclave safe to receive secrets. Here, the signed evidence correctly identified the enclave as debuggable, but the authorization policy ignored that fact.

The release loop encrypted every retained seed

After authorization, the registration ECALL selected the key from quote report_data and iterated the complete seed vector.

Vulnerable-source excerpt from registration/onchain.rs:147-164 (the first statement's line wrapping is normalized, // ... marks omitted upstream lines — a trace! call and the surrounding catch_unwind wrapper — and the block contains no Bluethroat commentary):

let target_public_key: &[u8; 32] =
    verified_quote.body.report_data.d[0..32].try_into().unwrap();

// ...

let seeds = KEY_MANAGER.get_consensus_seed().unwrap();
let mut res = Vec::new();

for s in &seeds.arr {
    let res_current: Vec<u8> = encrypt_seed(target_public_key, s, false)
        .map_err(|_| NodeAuthResult::SeedEncryptionFailed)?;
    res.extend(&res_current);
}

Each s is a 32-byte seed. encrypt_seed returns a 48-byte authenticated ciphertext, so two retained seeds produce a 96-byte response.

The newest seed feeds current confidentiality keys

The key manager derives state-encryption material from every seed and selects the last seed for current exchange keys.

Vulnerable-source excerpt from shared/utils/src/key_manager.rs:519-532. The block is verbatim upstream source, including its one comment; the analysis follows in prose.

for s in &self.consensus_seed.arr {
    // consensus_state_ikm
    self.consensus_state_ikm
        .arr
        .push(Self::generate_consensus_ikm_key(s));
}

let last_seed = &self.consensus_seed.last();

self.consensus_seed_exchange_keypair =
    Some(Self::generate_consensus_seed_exchange_keypair(last_seed));
self.consensus_io_exchange_keypair =
    Some(Self::generate_consensus_io_exchange_keypair(last_seed));

This made the newest vector element the active root for the current transaction I/O exchange key and associated state-encryption material.

Reproduction and evidence boundary

The result rests on two separate Azure SGX hardware witnesses:

Witness 1 — exact shipped and re-signed enclave. The exact shipped v1.26 enclave content was re-signed with DEBUG permitted and launched on Azure SGX. It retained production MRENCLAVE 2b9a69d6…, produced a genuine DEBUG quote and signed MAA token, returned two 48-byte ciphertext chunks, and exposed the registration private key through sgx-gdb. Both decrypted 32-byte seeds matched independent observations. Because an exact production enclave could not bootstrap a fresh private height-1 chain, a debugger guard supplied the successful certificate predicate for this run. The enclave, attestation path, seed loop, encryption, and debug recovery were the exact shipped/re-signed artifact; this witness did not exercise production block delivery.

Witness 2 — lab-built chain-context enclave. A production-feature enclave built from the exact v1.26 source supplied the private-chain context that Witness 1 could not create. Two lab-only substitutions admitted the initial validator set and mapped production validator addresses to deterministic lab signers. The registration, attestation, seed, FIFO, certificate-membership, and consumer code remained unchanged. The real SubmitBlockSignatures path populated VERIFIED_BLOCK_MESSAGES; the untouched membership predicate returned 96 ciphertext bytes for a certificate-bearing transaction and zero bytes with error 0x4255 for the matched certificate-absent transaction. This witness had a lab MRENCLAVE, not the shipped production measurement.

Together, the witnesses establish the measurement-preserving DEBUG re-sign, genuine attestation, exact release behavior, host-side recovery, and real verified-block membership gate. They do not constitute one end-to-end run against secret-4. Every decrypted seed, transaction fixture, and state fixture was synthetic. No live seed extraction was observed, and no transaction was broadcast to the live network.

Detailed hashes, quote fields, controls, and receipts are in the technical appendix.

Registration controls

The initial message path was low privilege, but it was not the only gate. RaAuthenticate.ValidateBasic accepted an ordinary signed transaction with a nonempty parseable certificate; it did not require validator, staking, delegation, or governance status. Before seed release, however, the trusted flow still required:

  • successful DCAP and MAA verification;
  • acceptance by the machine-admission mechanism;
  • matching MRENCLAVE; and
  • exact certificate membership in verified current-block data.

The vulnerability was not that arbitrary certificate-shaped input immediately returned seeds. It was that these later controls could authorize a genuinely attested but host-readable DEBUG enclave because the trusted policy reduced recipient safety to MRENCLAVE equality.

September observations of public documentation, endpoint versions, registration transactions, and non-validator registration state were point-in-time snapshots. They support reachability analysis, not a claim that an external admission opportunity remained continuously available.

Resolution

Maintainers stated that the shipped v1.27 line:

  • rejects a DEBUG quote before releasing any seed;
  • no longer treats an MAA token as join authorization;
  • removes the hosted Azure attestation helper; and
  • registers nodes through a transaction-authorized path.

Version v1.27.2 was released on 2026-09-23 and activated on secret-4 at height 27,286,266 under governance proposal 377; v1.27.3 later retained the enclave.

The public v1.27.2 source tag corroborates the first two properties. Its quote verifier rejects a DEBUG quote under the hardware-mode feature, and its allowlist decision states that a JWT is not authorization.

Verbatim upstream source (cosmwasm/enclaves/execute/src/registration/attestation.rs:884-892, Secret3dev/SecretNetwork@v1.27.2; the first comment line is upstream):

// Copy packed sgx_quote_t.report_body; a reference is E0793 (unaligned).
#[cfg(feature = "SGX_MODE_HW")]
{
    let quote_body = (*my_p_quote).report_body;
    if (quote_body.attributes.flags & SGX_FLAGS_DEBUG) != 0 {
        error!("DEBUG quote rejected before seed");
        return Err(sgx_status_t::SGX_ERROR_UNEXPECTED);
    }
}

The same tag also compares the quote's MRSIGNER with the local enclave's under hardware mode (registration/onchain.rs:77-81). Bluethroat did not rebuild or test the signed enclave running on secret-4, so these are source-tag observations rather than reproduced results, and the remaining two properties (hosted-helper removal and the transaction-authorized registration path) remain maintainer-reported.

For the vulnerable v1.26 design, the essential trusted-side invariant is straightforward: reject SGX_FLAGS_DEBUG before encrypting secrets, in the same authorization decision that evaluates enclave identity. Product ID and minimum security version should likewise be explicit policy inputs where the threat model relies on them.

Timeline

  • 2026-09-06 — The exploit chain and controls were captured on Azure SGX using the two complementary hardware witnesses.
  • 2026-09-08 — Bluethroat reported the finding to Secret Network maintainers under coordinated disclosure.
  • 2026-09-21 — Governance proposal 377 submitted to upgrade secret-4 to v1.27.2 at height 27,286,266; it passed on 2026-09-22.
  • 2026-09-22 — Maintainers confirmed remediation, stating that DEBUG quotes are rejected before seed release and that MAA tokens are not join authorization.
  • 2026-09-23 — v1.27.2 released; secret-4 upgraded at height 27,286,266, block time 12:29:45 UTC (v1.27.3 followed later and retained the enclave).
  • 2026-10-15 — Maintainers cleared the finding for publication.

Key takeaway

MRENCLAVE answers whether an enclave contains expected initial code and data. It does not answer whether the enclave is protected from its launching host. SGX reports DEBUG separately because it changes that security property.

Attestation policy must evaluate every claim on which secret-recipient safety depends. In this case, the evidence was genuine and even stated is-debuggable=true; the failure was authorizing on the measurement while ignoring the attribute that determined whether the host could read the recipient's memory.

References

Technical Appendix: REGDBG-001

This appendix records provenance, hardware receipts, and controls for Secret Network Consensus-Seed Disclosure Through DEBUG Enclave Re-Signing. Values are drawn from the captured disclosure bundle. Lab identities, synthetic seeds, and synthetic keys are identified as such.

1. Pinned revisions

ItemValue
RepositorySecret3dev/SecretNetwork
Release tagv1.26.0-community-continuance
Pinned source commit2b8e18788e70d79245343ed53bc2cc42a345d2d4
September 2026 secret-4 endpoint snapshotv1.26.0 at build commit 4aebd17c3b11c574d7dad07056ee6bd30b48e140
Public v1.27 source snapshot91145f355c45fd2464ec486fc800a986cd3b5e2b
Remediated releasev1.27.2, released 2026-09-23
Deployment statusActivated on secret-4 at height 27,286,266 (2026-09-23 12:29:45 UTC), governance proposal 377

The following load-bearing files were byte-identical across the pinned v1.26 tree, the build commit reported by the September endpoint snapshot, and the public v1.27 source snapshot:

  • cosmwasm/enclaves/execute/src/registration/onchain.rs
  • cosmwasm/enclaves/execute/src/registration/attestation.rs
  • cosmwasm/enclaves/shared/crypto/src/dcap.rs
  • cosmwasm/enclaves/execute/src/registration/seed_exchange.rs
  • cosmwasm/enclaves/shared/utils/src/key_manager.rs
  • x/registration/internal/keeper/msg_server.go
  • x/registration/internal/keeper/keeper.go

Relevant source anchors in the pinned tree are:

  • registration identity check: registration/onchain.rs:44-74;
  • seed release loop: registration/onchain.rs:147-165;
  • seed encryption and inverse: registration/seed_exchange.rs:9-100;
  • seed-vector creation and current-seed selection: key_manager.rs:385-394 and key_manager.rs:501-532; and
  • derivation domains for seed exchange, I/O exchange, and state IKM: shared/crypto/src/consts.rs:65-67.

Maintainers described the v1.27.2 remediation. The public v1.27.2 source tag (72c548f39602e87924f69289715ae8d7c8cfa7b4) shows the DEBUG-quote rejection at registration/attestation.rs:884-892 and an MRSIGNER comparison at registration/onchain.rs:77-81, but Bluethroat did not rebuild or test the signed enclave running on secret-4. The report makes no reproduced-result claim for that release, and the hosted-helper removal and transaction-authorized registration path remain maintainer-reported.

2. Release artifact provenance

  • Exact release enclave input SHA-256: 4fc1489204f63a9dfbec40a48c57fff162851685cf669b6ddf5f1d544c705699.
  • Re-signing preserved the measured pages while changing the signer and attribute mask.
  • The expected and observed invariant was an unchanged production MRENCLAVE, a new attacker-controlled MRSIGNER, and a SIGSTRUCT attribute mask permitting DEBUG.
  • The official production SIGSTRUCT rejected a forced DEBUG launch with SGX_ERROR_NDEBUG_ENCLAVE. The attack therefore required re-signing; it did not turn an unchanged production signature into a DEBUG signature.

3. Hardware witness A: exact shipped/re-signed enclave

This witness used the exact shipped v1.26 enclave content, re-signed under a lab key with DEBUG permitted, on Azure SGX. It established preservation of the production measurement, genuine attestation of the DEBUG artifact, exact receiver behavior, and host-side key recovery.

Genuine quote summary, captured 2026-09-06:

{
  "debug_bit_set": true,
  "mode64bit_bit_set": true,
  "quote_attributes_flags_hex": "0x7",
  "quote_version": 3,
  "isvprodid": 0,
  "isvsvn": 3,
  "mrenclave": "2b9a69d612bbaa4d038cf2ae8c72245dabacd0650c327d2e25dffdd979dfa80d",
  "mrsigner": "c9862d4a496494a6d2fc2b401593b6f9b867a3b7e8b4b3791920385531cd048b",
  "report_data_first32_equals_registration_public_key": true,
  "report_data_second32_all_zero": true,
  "quote_sha256": "ff535349d5b25933d145acf1d8cb5bf23073f9706b598a758de83d46ef8d65c8"
}

Observed results:

  • Official and re-signed MRENCLAVE values were equal: 2b9a69d612bbaa4d038cf2ae8c72245dabacd0650c327d2e25dffdd979dfa80d.
  • The MRSIGNER above belonged to the lab signer and differed from the official signer.
  • The West Europe MAA token had a valid RS256 signature, was bound to the submitted quote, and reported is-debuggable=true.
  • The exact receiver returned two 48-byte encrypted seed chunks, for 96 bytes total.
  • sgx-gdb recovered the registration private key from the DEBUG enclave.
  • Decryption yielded two distinct 32-byte synthetic canary seeds. Their hashes matched independent debugger observations.

An exact production enclave could not bootstrap the validator context for a fresh private height-1 chain. For this witness only, a debugger guard supplied a successful result for check_cert_in_current_block(). The enclave artifact, attestation, QVL/QVE processing, MAA verification, identity check, report-data handling, seed iteration, encryption, and host recovery were the exact shipped/re-signed implementation. Production block delivery was not exercised by this witness.

4. Hardware witness B: lab-built chain context

This witness used a production-feature enclave built from the exact pinned v1.26 source. It supplied the chain context unavailable to the exact shipped enclave in Witness A.

Two lab-only substitutions:

  1. admitted the initial validator set that a synchronized production enclave would already possess; and
  2. mapped five production validator addresses to deterministic lab signers.

No registration, attestation, MAA, seed, transport, FIFO, certificate-membership, or consumer code was changed. SubmitBlockSignatures populated VERIFIED_BLOCK_MESSAGES, and check_cert_in_current_block remained untouched.

Positive receipt:

{
  "result": "STOCK_FIFO_DEBUG_CERT_ALL_SEEDS_RELEASED",
  "txraw_contains_exact_certificate": true,
  "encrypted_seed_bytes": 96,
  "encrypted_seed_48byte_chunks": 2,
  "certificate_sha256": "89492906e41e80f9b4f6f1f4bf8fc6387ca19c0342b72174096eb0dc1de811a1",
  "encrypted_seed_sha256": "d3a8b94da42e474c7d6400f15456864add7ad12ae8b8e862bc808c6c05b7eec7"
}

The 96-byte response comprised two 48-byte authenticated ciphertext chunks. Each chunk encrypted one 32-byte synthetic seed.

Lab enclave identity and attestation summary:

quote flags: 0x7
MRENCLAVE: 1e1d75907b86750c085731e5f10f3d1df2f54c591b2e5a874452cc6349341541
MRSIGNER:  f7c6fa54e1064994371182ccefb2244544106b651c3ebeb8f66ec9f6329bb78d
MAA RS256 signature: valid
is-debuggable: true
tampered signature: rejected

The receiver and DEBUG target shared the lab MRENCLAVE and differed in signer; only the target SIGSTRUCT permitted DEBUG. This was a lab-built enclave and did not carry the production measurement.

5. Matched block-membership control

From the same sealed snapshot as Witness B, a valid signed TxRaw without the certificate reached the unchanged current-block predicate, logged Failed to validate message, error 0x4255, and returned zero seed bytes. The paired positive and negative differed in whether the exact certificate bytes were present in verified block data.

This isolates verified-block membership as a later release control. It also qualifies the initial transaction-path observation: valid signature and parseable certificate were sufficient for basic message acceptance, but not for seed release.

6. Attestation controls

  • A one-byte mutation of the MAA JWT signature produced no seed output.
  • The quote's first 32 report-data bytes equaled the recovered registration private key's public half.
  • The genuine MAA token was cryptographically valid and bound to the exact quote.
  • The token explicitly reported is-debuggable=true; the vulnerable authorization path did not consume that claim.
  • A production-signed enclave could not simply be launched with DEBUG. Measurement-preserving re-signing under another signer was necessary, and the vulnerable identity policy did not reject the signer change or DEBUG attribute.

7. External recovery and consumers

The host-side consumer decrypted both hardware-produced ciphertext chunks to distinct 32-byte synthetic seeds. From the newest seed it:

  • derived the receiver's recorded current I/O public key 923abb7e706d78d7af86cb15f69ea674a299cb703f07e905c4c197fd7a8f2224;
  • decrypted a synthetic current-format transaction; and
  • reproduced the v1.26 current state key/value format for a synthetic state fixture.

The older genesis seed failed the same current transaction and state controls.

Consumer receipt:

registration_recipient_recovered_full_vector=true
current_transaction_genesis_seed_rejected=true
current_state_genesis_seed_rejected=true
RESULT=RECOVERED_NEWEST_REGISTRATION_SEED_DECRYPTS_CURRENT_IO_AND_STATE

The external consumer used crates.io bincode2 2.0.1. Its src/ tree was byte-identical to SGX fork commit bdf9f458eaf41778d64cb812ed8fcad64ffd72a9 pinned in the enclave Cargo.lock; only dependency wiring differed to permit an outside-SGX build.

8. Registration-path observations

RaAuthenticate.ValidateBasic required a valid sender and a nonempty parseable certificate. It did not require validator, stake, delegation, or governance status. The message server then passed the certificate and replacement field into the registration flow.

That basic acceptance was followed by independent controls:

  • quote and MAA verification;
  • machine-admission policy;
  • MRENCLAVE comparison; and
  • exact certificate membership in verified current-block data.

Read-only September 2026 snapshots of public documentation and secret-4 showed that Azure SGX full nodes were documented and that non-validator accounts could have completed registration state. Separate registration-transaction snapshots contained parseable certificate structures. These observations establish point-in-time path reachability, not a claim that registration remained continuously available or that live exploitation succeeded.

9. Evidence limits and safety

  • Every seed released and decrypted was a synthetic canary created in a lab keychain.
  • No live secret-4 seed extraction was observed.
  • No live transaction or contract state was decrypted.
  • No transaction was broadcast to Secret mainnet or testnet.
  • No single run combined a fresh production-measurement DEBUG re-sign, production block-delivered registration, and live seed recovery.
  • Witness A used the exact shipped/re-signed enclave but supplied the certificate predicate with a debugger guard.
  • Witness B exercised the real FIFO and unchanged certificate-membership predicate but used a lab-built enclave with a lab measurement and explicit chain-bootstrap substitutions.
  • The disclosure bundle contains no Azure credential, SSH key, enclave-signing RSA private key, production consensus seed, or live-network secret.
  • The synthetic DEBUG registration private key is deliberate proof-of-concept output. Its public half is authenticated by the captured quote and permits independent decryption of the synthetic receipt.

These boundaries are why the report describes a demonstrated confidentiality-boundary failure without claiming that live seed extraction occurred.