Finding 5/6: Trusted execution over an unsafe transport layer.
TLS Certificate Verification Unconditionally Disabled for PCCS Collateral Fetching
Severity: Medium
Affected component: dcap-qvl collateral fetching (PCCS/PCS HTTP client)
Finding ID: #5 from the dstack/Phala assessment
The HTTP client used by dstack's DCAP quote verification library to fetch Intel collateral had TLS certificate verification unconditionally disabled. Every deployment, including those connecting to Intel's production PCCS endpoints, silently accepted any certificate, including self-signed, expired, or attacker-controlled certificates. The setting was hardcoded with no opt-out.
The issue was identified by Bluethroat Labs and responsibly disclosed to Phala Network in January 2026.
Executive Summary
- The
build_http_client()function indcap-qvlsetdanger_accept_invalid_certs(true)unconditionally on thereqwestHTTP client. - This disabled TLS certificate verification for all outbound collateral requests: PCK certificates, CRLs, TCB Info, and QE Identity.
- An on-path attacker (network MITM) could intercept these requests without needing a valid certificate.
- Intel's offline signature verification protected most collateral types against forgery, but did not protect against rollback attacks or QE Identity forgery (the latter documented in a separate finding).
- No exploitation of this vulnerability was observed during the assessment. The damage described below was possible but did not occur.
Background
Intel's DCAP attestation verification depends on collateral: supplementary data that the verifier fetches from a Provisioning Certification Service (PCS) or a locally cached PCCS server. This collateral includes:
- PCK Certificate Chain: proves the attestation key traces to Intel's root CA
- Certificate Revocation Lists (CRLs): identifies revoked certificates
- TCB Info: maps platform SVN values to security posture status
- QE Identity: defines the expected identity of the Quoting Enclave
The collateral is fetched over HTTPS. TLS serves two purposes in this flow: it authenticates the server (proving the response comes from Intel or a trusted PCCS), and it protects freshness (preventing an attacker from replaying older responses).
Intel also signs most of these collateral items with an offline key, providing a second layer of protection. That offline signature means an attacker who intercepts the transport cannot forge TCB Info or CRLs from scratch. But TLS and offline signatures protect against different things, and disabling TLS removes protections that offline signatures do not replace.
Vulnerability Details
The Hardcoded Setting
The entire collateral-fetching path used a single HTTP client builder. TLS verification was disabled unconditionally:
// dcap-qvl/src/collateral.rs, lines 247-255
fn build_http_client() -> Result<reqwest::Client> {
let builder = reqwest::Client::builder();
#[cfg(not(feature = "js"))]
let builder = builder
.danger_accept_invalid_certs(true) // TLS verification disabled
.timeout(Duration::from_secs(180));
Ok(builder.build()?)
}The danger_ prefix in the reqwest API exists specifically to signal that this option should be used with care. Here it was applied as the default for every build, every deployment, every collateral request.
Where It Was Used
The HTTP client was used for all collateral fetching:
// dcap-qvl/src/collateral.rs, lines 282-303
pub async fn get_collateral(pccs_url: &str, mut quote: &[u8]) -> Result<QuoteCollateralV3> {
let parsed_quote = Quote::decode(&mut quote)?;
let client = build_http_client()?; // Client with disabled TLS
let pck_chain = get_pck_chain(&client, pccs_url, &parsed_quote).await?;
// ...
}Multiple collateral endpoints were contacted with this client:
// dcap-qvl/src/collateral.rs, lines 329-348
let response = client.get(endpoints.url_pckcrl()).send().await?; // PCK CRL
let response = client.get(endpoints.url_tcb()).send().await?; // TCB Info
let response = client.get(endpoints.url_qe_identity()).send().await?; // QE IdentityThis included connections to Intel's production PCS at api.trustedservices.intel.com. There was no configuration knob, no environment variable, no feature flag to re-enable verification.
What the Offline Signatures Protect (and What They Don't)
This is where the nuance matters. Intel signs most collateral offline, and dcap-qvl does verify those signatures:
// dcap-qvl/src/verify.rs, lines 182-203
let tcb_certs = extract_certs(collateral.tcb_info_issuer_chain.as_bytes())?;
verify_certificate_chain(&tcb_leaf_cert, tcb_chain, now, &crls, trust_anchor.clone())?;
tcb_leaf_cert.verify_signature(
webpki::ring::ECDSA_P256_SHA256,
collateral.tcb_info.as_bytes(),
&asn1_signature,
)?;This means an attacker who intercepts the connection cannot fabricate entirely new TCB Info or CRLs. Those items are bound to Intel's root CA and will fail offline verification if tampered with.
However, offline signatures do not protect against everything:
| Attack | Offline Signatures Prevent? | TLS Prevents? |
|---|---|---|
| Forging new TCB Info | Yes | Yes |
| Forging new CRLs | Yes | Yes |
| Replaying older-but-still-valid collateral | No | Yes |
| Forging QE Identity (separate finding) | No | Yes |
| Denial of service via malformed responses | No | Yes |
| Routing requests to attacker-controlled server | No | Yes |
The critical gap is rollback attacks.
What Could Have Gone Wrong (But Did Not)
No exploitation of this vulnerability was observed during the assessment. The following scenarios describe damage that was possible given the code as written, not damage that occurred.
Rollback Attacks
Intel collateral has validity windows. TCB Info and CRLs include a nextUpdate timestamp that can span days or weeks. An on-path attacker could serve older collateral from before a security advisory was published. Because that older collateral would still carry a valid Intel signature and a nextUpdate timestamp that had not yet expired, dcap-qvl would accept it.
The practical consequence: a verifier could accept quotes from a platform whose TCB status Intel had already downgraded, because the verifier was seeing pre-advisory collateral replayed by an attacker. The verifier would have no way to know it was evaluating against stale data.
QE Identity Forgery (Compounding Effect)
This finding compounds with a separate issue (documented independently): QE Identity data fetched from PCCS was not cryptographically verified by dcap-qvl's offline verification path. With TLS also disabled, QE Identity had no protection at all, neither transport-layer nor application-layer. An on-path attacker could serve entirely fabricated QE Identity data, and the verifier would accept it.
Denial of Service
An attacker with a network MITM position could redirect collateral requests to an attacker-controlled server, serve malformed responses that cause parsing failures, or introduce arbitrary latency. This would prevent the verifier from completing attestation, effectively denying service to any workload that depends on DCAP verification.
What This Did Not Allow
To be clear about the boundaries: an on-path attacker could not forge entirely new TCB Info, CRLs, or PCK certificate chains. Intel's offline signatures prevented that. The attacker could not create attestation quotes out of thin air. And the attacker could not bypass CPU-side attestation, which followed a separate verification path.
The risk was narrower but real: replay of stale-but-valid collateral, forgery of unprotected QE Identity data, and denial of service.
Why We Classify This as Medium
The Medium severity reflects the interplay between what was exposed and what was still protected.
First, the setting was unconditional. There was no way for any deployment to re-enable TLS verification. Every instance of dcap-qvl in every dstack deployment connected to PCCS/PCS with certificate verification disabled, including connections to Intel's production service.
Second, rollback attacks are a real class of attack against attestation collateral. The validity windows on Intel collateral are not instantaneous; they span days to weeks. An attacker who can replay pre-advisory collateral during that window can cause verifiers to accept quotes that Intel's latest advisory would reject.
Third, this compounds with the QE Identity verification gap. Disabling TLS removed the last remaining protection for QE Identity data that was already unprotected at the application layer.
The reason this is Medium rather than High is that Intel's offline signature verification still prevented the most severe attack (complete collateral forgery), and exploitation required an on-path network position.
Fix
Remove the unconditional danger_accept_invalid_certs(true) and default to standard TLS verification. For deployments that use private PCCS servers with self-signed certificates, provide an explicit opt-in mechanism (such as an environment variable or configuration flag) rather than disabling verification for all deployments by default.
fn build_http_client() -> Result<reqwest::Client> {
let builder = reqwest::Client::builder();
#[cfg(not(feature = "js"))]
- let builder = builder
- .danger_accept_invalid_certs(true)
- .timeout(Duration::from_secs(180));
+ let builder = {
+ let builder = builder.timeout(Duration::from_secs(180));
+ if std::env::var("DCAP_QVL_SKIP_TLS_VERIFY").is_ok() {
+ builder.danger_accept_invalid_certs(true)
+ } else {
+ builder
+ }
+ };
Ok(builder.build()?)
}Custom CA bundle support would be a more robust alternative for private PCCS deployments, avoiding the need to disable verification entirely.
Timeline
| Date | Event |
|---|---|
| January 2026 | Finding reported to Phala Network |
| January 2026 | Acknowledged and triaged |
| February 2026 | Phala publishes security update covering attestation pipeline hardening |
Key Takeaway
TLS and offline signatures protect against different attack classes. Offline signatures prevent forgery of new data. TLS prevents replay of old data. Disabling TLS, even when offline signatures are checked, leaves the door open to rollback attacks: an attacker serving stale-but-validly-signed collateral that predates a security advisory. In an attestation pipeline, freshness is a security property, not a convenience feature.
References
- Original assessment finding:
code/submitted_findings.mdfinding#5 - reqwest
danger_accept_invalid_certsdocumentation:https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.danger_accept_invalid_certs
This vulnerability was discovered by Rahul Saxena of Bluethroat Labs during a security assessment of the dstack ecosystem. Responsible disclosure was coordinated with Phala Network.
