The Secret Network Research Path
The Approval Governance Never Made
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
Secret3devorganization. All observations reflect the audited revisions above. No transaction was broadcast tosecret-4, and no production key-manager material or user plaintext was accessed.
The chain, in five steps
- Prepare. The attacker controls an active-set validator, a contract able to emit Stargate messages, and an SGX target with an attacker-chosen MRENCLAVE.
- Propose. In one proposer slot, the attacker includes a valid contract
execution and a raw top-level witness carrying the same
MsgUpgradeProposalPassedvalue. - Bypass. SGX records the witness's raw
Any.Value. The contract message then reachesMsgServer.UpgradeProposalPassedwithout the top-level governance-to-MRENCLAVE comparison. - Persist.
ecall_onchain_approve_upgradefinds the raw witness and stores the attacker MRENCLAVE inKEY_MANAGER.extra_data.next_mr_enclave. Normal execution later rejects the witness withno latest upgrade proposal. - 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
| Layer | Establishes | Does not establish |
|---|---|---|
| Source and route preflight | Contract Stargate reaches the sensitive message server while top-level ante enforces governance | SGX export or production deployment |
| Four-validator software different-target run | Common committed block; contract success/witness rejection split; attacker-selected approval; export from source; import into a distinct target | Real SGX hardware or a live mainnet key manager |
| Azure SGX self-target run | Real-hardware approval and export; migration payload written by the SGX path | Different-target import on hardware or the full mainnet feature set |
| Full-feature Azure attempts | Mainnet-like builds completed and image provenance was retained | Exploit reachability; both localnets stalled at height 1 |
| Public source review | The affected paths are included under the relevant mainnet gates; KEY_MANAGER.serialize includes seeds and the registration private key | A post-import application-ciphertext decryption witness |
No single run executes the entire sequence with a production
secret-4key 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:
- contract Stargate rejects both privileged compute message type URLs;
MsgServer.UpgradeProposalPassedalways returnsErrUpgradeProposalPassedDenied;ecall_onchain_approve_upgradeno longer writes the supplied measurement;- migration export ignores
next_mr_enclaveand 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 onsecret-4at height 27,286,266. - 2026-11-10 — Bluethroat verified the message denial, no-write ECALL, and
export-policy fixes in the public
v1.27.2source 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
- Secret Network
v1.24.1source tag - Contract Stargate dispatch
- Top-level upgrade verification
- Vulnerable upgrade message server
- Vulnerable approval persistence
- Vulnerable MRENCLAVE-only export policy
KEY_MANAGERserialization- Secret Network
v1.27.2release v1.27.2privileged-message checkerv1.27.2non-persistent approval ECALL
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
| Component | Revision |
|---|---|
| Confirmed affected release by source review | scrtlabs/SecretNetwork v1.24.1, commit 266cd4fd96e816656f520ba85d309cc5b1c78ef9 |
| Primary software witness checkout | edcbccbbbe65cae1bd93d4d9089ff5d98228f704 |
Public v1.25.0 tag | 9f08b0e503cb439708b34399df9349864d0a62ce |
| Community remediation source | Secret3dev/SecretNetwork v1.27.2, commit 72c548f39602e87924f69289715ae8d7c8cfa7b4 |
| Software positive-run mode | SGX software mode, different source/target measurements |
| Hardware positive-run features | light-client-validation,random |
| Planned mainnet-like features | verify-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.UpgradeProposalPasseddoes 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 anythingIf 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 4All four block-results summaries recorded:
tx0 code=0
tx1 code=1 log=no latest upgrade proposalThe 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=trueThe 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 succeededThe distinct target logged:
Import sealed data from the previous enclave
Sealing data successfully imported
Migration op succeededArtifact 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 importThis 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 2c04ef94838c3597c23352a8fc1c87797a35e32a105f0f65f5cd6bcb0e5cc936Honest endpoints captured the same height-22 block:
block hash
529F8588F6B17B1173C4D6BAA4C8192C4C4CAB9D9EF92B8FF0A43D35C584B5CA
tx0 code=0
tx1 code=1 log=no latest upgrade proposalThe successful migration operation logged:
Migration is authorized by on-chain consensus.
Sealed data successfully exported
Migration op succeededResulting identities:
migration_data.bin
7b86c62bcfd9ebf2a2085912623a7c1fd285f344674edd291a2d7ac000921c69
migration_report_local.bin
831c540be755414c27ccf4e062080a07e14a67e7ff712096d10d7dc3fe386f3dAn 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_UNEXPECTEDThe 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,randomand a fallback:
verify-validator-whitelist,light-client-validation,randomBoth 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:
- the keychain format version;
- every consensus seed;
- the registration private key when present;
- height and serialized validator-set state;
next_mr_enclaveand 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 path | Observed effect |
|---|---|
Contract stargate feature | Enables the generic SDK-message encoder and dispatcher |
| Top-level ante | Applies governance verification to top-level MsgUpgradeProposalPassed only |
light-client-validation | Requires the matching raw block-message witness; the attack supplies it |
production | No affected dispatch, message-server, approval, or export line was made safe by this gate in the audited source |
verify-validator-whitelist | Governs separate validator-whitelist and emergency-consensus paths; it did not restore the missing message-server check |
random | Enabled in both primary runs; it is not the authorization boundary |
debug-print | A 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.tsvThese 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-66identifies both sensitive messages as privileged.handler_plugin.go:463-483rejects privileged Stargate type URLs before unpacking and routing.privileged.go:49-80,215-248denies the upgrade message directly and through nestedAnywrappers.msg_server.go:211-213rejects every directUpgradeProposalPassedcall.offchain.rs:684-708checks the raw block witness but does not store the supplied measurement.offchain.rs:1250-1261authorizes 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.

