Footer
General•November 11, 2026

The Secret Network Research Path

The Approval Governance Never Made

By Rahul Saxena (@saxenism)

How Stargate bypasses governance, raw witnesses persist approval, and SGX exports keys

Severity: Critical (unauthorized target approval and different-target key-manager export/import were demonstrated; production reachability depends on an active-set Byzantine proposer, a legitimately seeded validator, and the source-derived mainnet feature bridge).

Affected component: the then-current scrtlabs/SecretNetwork repository, release v1.24.1 at commit 266cd4fd96e816656f520ba85d309cc5b1c78ef9, now publicly maintained as Secret3dev/SecretNetwork; the primary software witness ran against descendant commit edcbccbbbe65cae1bd93d4d9089ff5d98228f704. Affected paths include contract Stargate dispatch, upgrade approval, raw SGX block witnesses, and migration export. Full revision table in the technical appendix.

Fix: The Secret3dev community's v1.27.2 denies MsgUpgradeProposalPassed through every inspected message path, makes its SGX ECALL non-persistent, and authorizes migration export only through the emergency-consensus file. Released and activated on secret-4 on 2026-09-23.

Disclosure: first submitted to Secret Labs under coordinated disclosure on [DATE TO CONFIRM]; later fixed by community maintainers in the Secret3dev organization; fix released and activated on mainnet 2026-09-23.

Secret Network let contracts emit registered SDK messages through Stargate. For MsgUpgradeProposalPassed, the contract route checked basic validity and that the contract was the signer, but skipped the top-level ante check that compared the proposed MRENCLAVE with the latest passed governance upgrade.

The SGX approval path also required matching bytes in the current block. A Byzantine proposer could supply those bytes in a second top-level transaction that later failed normal SDK execution. SGX captured the raw value first, persisted the attacker-selected MRENCLAVE as next_mr_enclave, and later treated that value alone as authorization to export the validator's serialized KEY_MANAGER.

This writeup describes a vulnerability first disclosed by Bluethroat Labs to Secret Labs under coordinated disclosure and later remediated by the community maintaining Secret Network in the Secret3dev organization. All observations reflect the audited revisions above. No transaction was broadcast to secret-4, and no production key-manager material or user plaintext was accessed.

The chain, in five steps

  1. Prepare. The attacker controls an active-set validator, a contract able to emit Stargate messages, and an SGX target with an attacker-chosen MRENCLAVE.
  2. Propose. In one proposer slot, the attacker includes a valid contract execution and a raw top-level witness carrying the same MsgUpgradeProposalPassed value.
  3. Bypass. SGX records the witness's raw Any.Value. The contract message then reaches MsgServer.UpgradeProposalPassed without the top-level governance-to-MRENCLAVE comparison.
  4. Persist. ecall_onchain_approve_upgrade finds the raw witness and stores the attacker MRENCLAVE in KEY_MANAGER.extra_data.next_mr_enclave. Normal execution later rejects the witness with no latest upgrade proposal.
  5. Export. On the attacker's legitimately seeded validator, migration export accepts a report matching that stored MRENCLAVE, serializes KEY_MANAGER, and encrypts it to the target enclave.

The primary software run committed the two-transaction block on four validators, approved a target measurement different from the source, exported the key manager, and imported it into that target. A separate Azure hardware run confirmed approval and export on real SGX using a self-target measurement. Receipts are in the technical appendix.

Why this is a trust-root problem

MsgUpgradeProposalPassed was intended to connect on-chain governance to an SGX migration target. The application enforced that connection only in top-level ante handling, while contract Stargate called the message server through a different route.

SGX then used raw block-message presence as evidence that the target was approved. Data-hash inclusion established only that the proposer placed the bytes in the block. It did not establish that governance approved them or that the application accepted the witness transaction.

The conservative attack path does not require an honest operator to run migration. A malicious active-set validator can export from its own legitimate, seeded validator enclave after the forged approval is persisted.

Security property violated. Migration of network key-manager state must be authorized by the governance decision and authenticated enclave policy, not by a contract-routed message plus proposer-selected raw bytes.

Root cause

1. Contract Stargate skipped the top-level governance check

The contract message handler validated the decoded SDK message and required the contract address to be its signer, then dispatched it. It did not call the compute ante decorator.

Vulnerable-source excerpt (handler_plugin.go:580-603); ... marks omitted routing code:

if m, ok := msg.(sdk.HasValidateBasic); ok {
    if err := m.ValidateBasic(); err != nil {
        return nil, errorsmod.Wrapf(err, "failed basic validation for message type %T", msg)
    }
}

signers, _, err := h.cdc.GetMsgV1Signers(msg)
// ...
for _, acct := range signers {
    if !contractAddr.Equals(sdk.AccAddress(acct)) {
        return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission")
    }
}

For top-level transactions, verifyUpgradeProposal found the latest passed software-upgrade proposal and compared its MRENCLAVE with the message. The message server repeated only ValidateBasic before calling api.OnUpgradeProposalPassed.

2. SGX trusted a raw witness that the SDK rejected

SubmitBlockSignatures validated the block data hash, parsed each outer transaction, and copied every message's raw Any.Value into VERIFIED_BLOCK_MESSAGES. That happened before ordinary transaction execution.

Verbatim vulnerable source (wasm_messages.rs:38-42):

pub fn append_msg_from_tx(&mut self, mut tx: Tx) {
    for msg in tx.take_body().messages {
        self.messages.push_back(msg.value);
    }
}

The positive runs measured the boundary directly: the contract transaction returned code 0, the top-level witness returned no latest upgrade proposal, and the SGX log recorded migration-target approval.

3. Persisted MRENCLAVE alone authorized export

The approval ECALL wrote the supplied measurement to extra.next_mr_enclave and saved KEY_MANAGER. Migration export later compared the target report only with that stored MRENCLAVE.

Verbatim vulnerable source (offchain.rs:1041-1051):

fn is_export_approved(report: &sgx_report_body_t) -> bool {
    // Current policy: we only check mr_enclave, mr_signer can be anything

    {
        let extra = KEY_MANAGER.extra_data.lock().unwrap();
        if let Some(val) = extra.next_mr_enclave {
            if val.m == report.mr_enclave.m {
                println!("Migration is authorized by on-chain consensus.");
                return true;
            }
        }
    }

The export path then serialized KEY_MANAGER, encrypted it for the target report, and wrote the migration payload.

Reproduction

LayerEstablishesDoes not establish
Source and route preflightContract Stargate reaches the sensitive message server while top-level ante enforces governanceSGX export or production deployment
Four-validator software different-target runCommon committed block; contract success/witness rejection split; attacker-selected approval; export from source; import into a distinct targetReal SGX hardware or a live mainnet key manager
Azure SGX self-target runReal-hardware approval and export; migration payload written by the SGX pathDifferent-target import on hardware or the full mainnet feature set
Full-feature Azure attemptsMainnet-like builds completed and image provenance was retainedExploit reachability; both localnets stalled at height 1
Public source reviewThe affected paths are included under the relevant mainnet gates; KEY_MANAGER.serialize includes seeds and the registration private keyA post-import application-ciphertext decryption witness

No single run executes the entire sequence with a production secret-4 key manager, the complete mainnet feature set, and a distinct hardware target. The evidence is compositional. Source review supplies the feature-gate and serialized-key contents between measured layers.

The primary synthetic inputs were an 81-byte MsgUpgradeProposalPassed value, a valid contract execution carrying it, a top-level witness transaction carrying the same value, and separate source and target enclave measurements. The proposer patch only inserted the prepared transaction pair; honest validator images were unmodified.

Identities, hashes, commands, and full receipts are in the technical appendix.

Key-manager export reachability

The software witness demonstrated distinct-target semantics: the target MRENCLAVE differed from the source, the source enclave logged authorization and export, and the target enclave imported the resulting migration payload. The hardware witness demonstrated that real SGX accepted the forged on-chain approval and produced migration_data.bin.

The required adversarial capability is one active-set validator selected as proposer and control of that validator's own seeded SGX node. The export step is local to the attacker's validator; honest operators need not run migrate_op. The target must run on SGX and present the approved MRENCLAVE, but the inspected vulnerable branch did not require its MRSIGNER to match.

[REASONABLE INFERENCE from cosmwasm/enclaves/shared/utils/src/key_manager.rs:97-136 and Secret Network's encrypted-state architecture] The serialized payload contains consensus seeds, the registration private key, validator-set state, and extra state. Exporting that material compromises the key-manager trust root. The bundle did not include a separate witness decrypting historical application ciphertext after import, so the exact downstream plaintext reach is not claimed as a measured result.

What this did not allow

  • No production key-manager export was attempted. All seeds, enclaves, contracts, and chains were researcher-controlled.
  • No SGX cryptographic break was required or demonstrated. The vulnerable enclave authorized its own migration path.
  • No application-ciphertext decryption was demonstrated after import. That consequence is source-grounded, not a separate positive PoC.
  • No full-feature hardware different-target run completed. The hardware positive witness was self-target and the full-feature localnets stalled before the trigger.
  • No complete end-to-end production run was performed. The Reproduction table states where composition stops.

Severity

Bluethroat Labs classification: Critical, with deployment qualifiers.

The primary witness demonstrated unauthorized approval, export, and import of a serialized key manager into an attacker-selected enclave measurement. The hardware witness independently confirmed the approval/export path on SGX. This is a confidentiality and TEE trust-root compromise, not a chain-halt claim.

Critical depends on three conditions: Byzantine active-set proposers are in scope, the attacker operates a legitimately seeded validator, and the source-reviewed path is reachable under production deployment policy. The complete mainnet feature set did not produce a positive localnet run, and no production-only policy outside the audited checkout could be observed.

Fix

The Secret3dev community's public v1.27.2 source closes the chain at four boundaries:

  1. contract Stargate rejects both privileged compute message type URLs;
  2. MsgServer.UpgradeProposalPassed always returns ErrUpgradeProposalPassedDenied;
  3. ecall_onchain_approve_upgrade no longer writes the supplied measurement;
  4. migration export ignores next_mr_enclave and requires the emergency-consensus file.

Verbatim patched message-server source (msg_server.go:211-213):

func (m msgServer) UpgradeProposalPassed(goCtx context.Context, msg *types.MsgUpgradeProposalPassed) (*types.MsgUpgradeProposalPassedResponse, error) {
    return nil, types.ErrUpgradeProposalPassedDenied
}

Verbatim patched export policy (offchain.rs:1250-1261):

fn is_export_approved(report: &sgx_report_body_t) -> bool {
    // Export is approved only from the emergency consensus file.
    let _ = report;
    if let Ok(f_in) = File::open(make_sgx_secret_path(FILE_MIGRATION_CONSENSUS).as_str()) {
        if is_export_approved_offchain(f_in, report) {
            println!("Migration is authorized by off-chain (emergency) consensus");
            return true;
        }
    }

    false
}

Bluethroat verified these changes against the public source tag. It did not rebuild or test the signed enclave running on secret-4.

Timeline

  • 2026-06-04 — Different-target software evidence and Azure SGX hardware evidence captured.
  • [DATE TO CONFIRM] — Finding first submitted to Secret Labs under coordinated disclosure with software, hardware, proof-of-concept, and integrity evidence.
  • 2026-09-23 — The Secret3dev community released v1.27.2; it was activated on secret-4 at height 27,286,266.
  • 2026-11-10 — Bluethroat verified the message denial, no-write ECALL, and export-policy fixes in the public v1.27.2 source tag.
  • [DATE TO CONFIRM] — Secret Labs maintainers identify this finding as eligible for publication, subject to draft review.

Key takeaway

Authorization belongs at the operation boundary, where every caller must pass it. An ante check protects top-level transactions; it does not protect the same message when contracts, authz, governance wrappers, or interchain paths can reach the handler another way.

Enclave evidence must also distinguish inclusion from acceptance. Here, a raw witness that governance rejected still became the evidence SGX used to persist an upgrade target. Once that target controlled key export, the bypass crossed from message routing into the network's confidentiality root.

References

Technical Appendix: Secret Network Upgrade-Approval Smuggling

Supporting material for the main writeup. This appendix records the revisions, source bridge, measured outputs, artifact identities, and limitations behind the article.

1. Pinned revisions

ComponentRevision
Confirmed affected release by source reviewscrtlabs/SecretNetwork v1.24.1, commit 266cd4fd96e816656f520ba85d309cc5b1c78ef9
Primary software witness checkoutedcbccbbbe65cae1bd93d4d9089ff5d98228f704
Public v1.25.0 tag9f08b0e503cb439708b34399df9349864d0a62ce
Community remediation sourceSecret3dev/SecretNetwork v1.27.2, commit 72c548f39602e87924f69289715ae8d7c8cfa7b4
Software positive-run modeSGX software mode, different source/target measurements
Hardware positive-run featureslight-client-validation,random
Planned mainnet-like featuresverify-validator-whitelist,light-client-validation,production,random

The finding was first submitted to Secret Labs. Its eventual remediation was shipped by community maintainers in the Secret3dev organization.

The earlier report described edcbccbb… as the v1.25.0 tag in its captured checkout. The current public tag resolves to 9f08b0e5…; this appendix therefore identifies edcbccbb… as the primary witness commit without assigning that tag to it.

The same vulnerable shapes were checked on public v1.24.1:

  • contract Stargate unpacks and routes arbitrary registered SDK messages;
  • upgrade governance verification exists in top-level ante;
  • MsgServer.UpgradeProposalPassed does not repeat it;
  • raw message values enter VERIFIED_BLOCK_MESSAGES;
  • approval persists next_mr_enclave;
  • migration export accepts a matching MRENCLAVE without checking MRSIGNER.

2. Source boundary

Contract route

x/compute/internal/keeper/handler_plugin.go decodes contract Stargate data to an SDK message, checks basic validity and signer equality, then calls the normal router. The affected path has no call to verifyUpgradeProposal.

Top-level route

x/compute/internal/keeper/ante.go detects a top-level MsgUpgradeProposalPassed, selects the latest passed software-upgrade proposal, extracts its MRENCLAVE, and rejects missing or mismatched approval.

Raw SGX witness

During BeginBlock, SubmitBlockSignatures validates the transaction list against the block data hash, parses each outer transaction, and appends every top-level message's raw Any.Value to VERIFIED_BLOCK_MESSAGES. This precedes normal SDK transaction execution.

Persisted approval and export

The approval ECALL compares the requested 32-byte measurement with an 81-byte raw witness value. On a match, it stores that measurement in KEY_MANAGER.extra_data.next_mr_enclave and saves the key manager.

The affected export policy contains the upstream statement:

Current policy: we only check mr_enclave, mr_signer can be anything

If a migration report matches the stored MRENCLAVE, export serializes the key manager and encrypts it for the target report.

3. Evidence layers

Layer 1 — four-validator software different-target run

The primary run used four equal-power validators and inserted the attack pair at height 22. All four endpoints captured the same block:

height             22
transaction count  2
encrypted_random   non-null
block hash         EB0ACDB183595D621A783FCBF317328F57450565D977C360F0951DA830738ABB
commit signatures  4

All four block-results summaries recorded:

tx0  code=0
tx1  code=1  log=no latest upgrade proposal

The first transaction was the valid contract execution. The second was the top-level raw witness. The split demonstrates that contract routing reached the message server while top-level ante rejected the same operation.

Source and target measurements:

source_mrenclave
a4fbde94563e9ef499ab0e21b3562a6f724ffc1eef76a7c223d805af8582e5c1

target_mrenclave
a28e69be713882b21f5e5f42838cfa65e6ce6421e368318b0d09be79a9479c19

target_report_mrenclave
a28e69be713882b21f5e5f42838cfa65e6ce6421e368318b0d09be79a9479c19

source_and_target_differ=true

The source validator logged:

Migration target approved. mr_encalve=<target measurement>
Found local migration report
Migration is authorized by on-chain consensus.
Sealed data successfully exported
Migration op succeeded

The distinct target logged:

Import sealed data from the previous enclave
Sealing data successfully imported
Migration op succeeded

Artifact hashes:

78d1b54220ad48ae9acfecadd1b078589fc7ed92ebeb55aede0e741f0c91275a
source-migration_target_info.bin

09122b69161bc82f7c61c7d7faedb2bb61c574d065bd97b4d1e86be0f4e430df
target-migration_report_for_source.bin

2b5d87a1f2fe2a89d59d5d505128ca8991a778687bc92db32336d8d6bac46cff
migration_data_for_different_target.bin

6cb638bf2a045e38e00f1691aea5eec122447dad301c552729d6fcf4d45d5bdf
target sealed state after import

This layer proves different-target approval, export, and import in software mode. It does not use production secrets or real SGX.

Layer 2 — supporting software self-target run

An earlier hardened run demonstrated the same block-level approval and local export path using a self-target measurement. It is retained for chronology and provenance; the different-target run is the stronger impact witness.

Layer 3 — Azure real-SGX hardware run

Environment:

Azure SKU             Standard_DC8s_v3
region                eastus, zone 2
SGX mode              HW
features              light-client-validation,random
insertion height      22
source/target          self-target
MRENCLAVE              2c04ef94838c3597c23352a8fc1c87797a35e32a105f0f65f5cd6bcb0e5cc936

Honest endpoints captured the same height-22 block:

block hash
529F8588F6B17B1173C4D6BAA4C8192C4C4CAB9D9EF92B8FF0A43D35C584B5CA

tx0  code=0
tx1  code=1  log=no latest upgrade proposal

The successful migration operation logged:

Migration is authorized by on-chain consensus.
Sealed data successfully exported
Migration op succeeded

Resulting identities:

migration_data.bin
7b86c62bcfd9ebf2a2085912623a7c1fd285f344674edd291a2d7ac000921c69

migration_report_local.bin
831c540be755414c27ccf4e062080a07e14a67e7ff712096d10d7dc3fe386f3d

An earlier migrate_op 1 attempt failed while obtaining DCAP quote configuration:

Error returned from the p_sgx_get_quote_config API. 0xe019
Error creating attestation report
migration op error: SGX_ERROR_UNEXPECTED

The subsequent local-report migrate_op 2 succeeded. The receipt therefore establishes real-SGX approval and export, not a successful remote DCAP exchange or different-target hardware import.

Layer 4 — mainnet-like feature attempts

The Azure campaign built:

verify-validator-whitelist,light-client-validation,production,random

and a fallback:

verify-validator-whitelist,light-client-validation,random

Both four-validator localnets stalled at height 1 before the trigger. Build and image provenance were retained, but these are negative reachability attempts, not positive exploit witnesses.

4. Exported data boundary

At v1.24.1, cosmwasm/enclaves/shared/utils/src/key_manager.rs:97-136 serializes:

  1. the keychain format version;
  2. every consensus seed;
  3. the registration private key when present;
  4. height and serialized validator-set state;
  5. next_mr_enclave and block-seed metadata.

offchain.rs:1341-1375 calls that serializer, encrypts the bytes to a key derived from the target migration report, and writes the migration payload.

The evidence proves that the payload was exported and imported into a distinct software target, and that real SGX produced the payload in a self-target run. It does not contain a separate post-import decryption of historical Secret Network application ciphertext.

5. Feature-gate bridge

Gate or pathObserved effect
Contract stargate featureEnables the generic SDK-message encoder and dispatcher
Top-level anteApplies governance verification to top-level MsgUpgradeProposalPassed only
light-client-validationRequires the matching raw block-message witness; the attack supplies it
productionNo affected dispatch, message-server, approval, or export line was made safe by this gate in the audited source
verify-validator-whitelistGoverns separate validator-whitelist and emergency-consensus paths; it did not restore the missing message-server check
randomEnabled in both primary runs; it is not the authorization boundary
debug-printA software harness aid; it is not required by the vulnerable route

The complete production bridge is source-derived because the full-feature localnets did not progress past height 1.

6. Evidence locations in the disclosure bundle

Primary software different-target run:

evidence/sw-4val-poc/
upgrade-approval-smuggling-sw-different-target-20260604T043000Z/

Supporting software self-target run:

evidence/sw-4val-poc/
upgrade-approval-smuggling-sw-dedicated-20260604T001500Z/

Azure hardware campaign:

evidence/azure-sgx-hw-poc/
secret-sgx-upgrade-approval-hw-20260604T052202Z/

Key software summaries:

evidence/DIFFERENT-TARGET-HARDENED-INDEX.md
evidence/DIFFERENT-TARGET-PROOF-SUMMARY.md
evidence/different-target-exchange-summary.env
evidence/block-H-summary.tsv
evidence/commit-H-summary.tsv
evidence/block-results-H-summary.tsv

These paths are provenance for a disclosure-bundle reviewer. They are not public download links.

7. Remediation verification

The public v1.27.2 tag was inspected on 2026-11-10:

  • x/compute/internal/types/privileged.go:9-18,64-66 identifies both sensitive messages as privileged.
  • handler_plugin.go:463-483 rejects privileged Stargate type URLs before unpacking and routing.
  • privileged.go:49-80,215-248 denies the upgrade message directly and through nested Any wrappers.
  • msg_server.go:211-213 rejects every direct UpgradeProposalPassed call.
  • offchain.rs:684-708 checks the raw block witness but does not store the supplied measurement.
  • offchain.rs:1250-1261 authorizes export only from the emergency-consensus file.

This is source-tag verification. Bluethroat did not rebuild the final signed enclave or exercise the deployed secret-4 binary in this editorial pass.

8. Limitations

  • The trigger requires one active-set Byzantine proposer.
  • The conservative impact path also requires that proposer to operate a legitimately seeded validator.
  • The positive software witness used different source and target measurements; the positive hardware witness was self-target.
  • The complete mainnet feature set did not reach the trigger in the Azure localnet.
  • Any production-only contract upload, code-ID, or type-URL policy outside the audited checkout could affect reachability.
  • Historical plaintext recovery after import is not separately demonstrated.

9. Safety

No transaction was broadcast to secret-4. The work used isolated local networks, synthetic key managers, controlled contracts, and researcher-operated SGX hardware. No production seed, sealed mainnet key manager, user plaintext, credential, private key, PPID-bearing quote, or unredacted cloud response is included here.