Every CVE Search/ Get API key

CVE-2026-100836

Edgeless Systems Contrast through 1.20.0 Denial of Service via ciphertextContainer

5.3 MediumFix available

Contrast through 1.20.0 contains a panic vulnerability in the transit-engine endpoint's ciphertextContainer.UnmarshalJSON function that fails to validate decoded ciphertext length before slicing. An authenticated workload with a valid mesh certificate can trigger a runtime panic by submitting a short base64-encoded ciphertext, causing log spam and request failures without crashing the process.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
None
Availability
Low

Exploit likelihood

0.18%Unchanged since Sep 27, 2026
90%50%10%1%

What each source reports

CVE List Score used 5.3

Edgeless Systems Contrast through 1.20.0 Denial of Service via ciphertextContainer

Contrast through 1.20.0 contains a panic vulnerability in the transit-engine endpoint's ciphertextContainer.UnmarshalJSON function that fails to validate decoded ciphertext length before slicing. An authenticated workload with a valid mesh certificate can trigger a runtime panic by submitting a short base64-encoded ciphertext, causing log spam and request failures without crashing the process.

Record
CVE-2026-100836
CVSS 4.0
5.3 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L
Weakness
CWE-129
Updated
2026-11-02
EUVDEUVD-2026-87831 5.3

Contrast through 1.20.0 contains a panic vulnerability in the transit-engine endpoint's ciphertextContainer.UnmarshalJSON function that fails to validate decoded ciphertext length before slicing. An authenticated workload with a valid mesh certificate can trigger a runtime panic by submitting a short base64-encoded ciphertext, causing log spam and request failures without crashing the process.

Record
EUVD-2026-87831
CVSS 4.0
5.3 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-3ccm-4qq2-5wrp 4.3

Constrata's coordinator transit engine `ciphertextContainer.UnmarshalJSON` panics on attacker-controlled short ciphertexts

Summary

ciphertextContainer.UnmarshalJSON decodes the third :-separated component of a vault:vX:base64... ciphertext and then unconditionally takes a 12-byte prefix slice for the AES-GCM nonce: c.nonce = fullCiphertext[:aesGCMNonceSize]. If the decoded blob is shorter than 12 bytes, the slice expression panics. The panic happens before any cryptographic operation, while the JSON body of the request is still being parsed inside the request handler. Because the handler is invoked from net/http's standard handler goroutine, the panic is recovered to a 500 response, but the request handler aborts mid-execution and the recovered panic appears in the Coordinator's logs. An authenticated workload that holds a valid mesh certificate for any WorkloadSecretID can trigger the panic at will, producing log spam, request-failure metrics, and a slow but cheap denial of service against the transit-engine endpoint.

Details

the panicking slice

coordinator/internal/transitengineapi/crypto.go:64-88:

// UnmarshalJSON umarshalls a json string to a ciphertextContainer holding the version prefix,
// decoded base64 nonce and ciphertext.
func (c *ciphertextContainer) UnmarshalJSON(data []byte) error {
	var encoded string
	if err := json.Unmarshal(data, &encoded); err != nil {
		return err
	}
	// Split "vault:vX:base64" format
	parts := strings.SplitN(encoded, ":", 3)
	if len(parts) < 3 {
		return fmt.Errorf("invalid ciphertext format")
	}
	version, err := extractVersion(parts[1])
	if err != nil {
		return fmt.Errorf("ciphertext version: %w", err)
	}
	c.keyVersion = version
	fullCiphertext, err := base64.StdEncoding.DecodeString(parts[2])
	if err != nil {
		return fmt.Errorf("decoding ciphertext: %w", err)
	}
	c.nonce = fullCiphertext[:aesGCMNonceSize]      // PANIC when len(fullCiphertext) < 12
	c.ciphertext = fullCiphertext[aesGCMNonceSize:]
	return nil
}

aesGCMNonceSize = 12 (defined at line 33). There is no length check on fullCiphertext. If parts[2] decodes to fewer than 12 bytes (which happens for any base64 string shorter than ~16 characters), the slice expression fullCiphertext[:aesGCMNonceSize] triggers Go's runtime panic runtime error: slice bounds out of range [:12] with length N.

UnmarshalJSON is reached from parseRequest:

// coordinator/internal/transitengineapi/transitengineapi.go:292-302
func parseRequest(r *http.Request, into any) error {
	defer r.Body.Close()
	if err := validateContentType(r); err != nil {
		return err
	}
	if err := json.NewDecoder(r.Body).Decode(into); err != nil {
		return err
	}
	return nil
}

which is called inside getDecryptHandler (line 178-237) before any other processing.

auth requirement is real but trivial to satisfy for any registered workload

The transit-engine HTTP server (transitengineapi.go:74-100) configures tls.RequireAndVerifyClientCert with the Coordinator's mesh CA pool. The handler is wrapped by authorizationMiddleware (line 348-357) which calls authorizeWorkloadSecret (line 241-254). That function reads the WorkloadSecretOID extension from the peer cert and requires it to match the URL path's {name} segment.

Any workload that has gone through the normal initializer / meshapi flow (coordinator/internal/meshapi/meshapi.go:71-119) and has a non-empty WorkloadSecretID in its PolicyEntry is issued a mesh cert with the matching extension, so the path-name authorisation is automatically satisfied for whichever workloadSecretID the manifest assigned to that workload. There is no rate limiting, no proof-of-work, and no audit log on triggering the panic.

what happens after the panic

net/http wraps each handler in a recovered goroutine, so the panic does not crash the Coordinator process. Instead:

  1. The Go runtime captures the panic, logs http: panic serving <peer>: runtime error: slice bounds out of range to stderr together with a goroutine stack trace.
  2. The connection is hung up without a response…
Record
GHSA-3ccm-4qq2-5wrp
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L
Weakness
CWE-129
Updated
2026-11-02
GitHub AdvisoriesGHSA-x66w-59x9-vvcf 5.3

Contrast through 1.20.0 contains a panic vulnerability in the transit-engine endpoint's...

Contrast through 1.20.0 contains a panic vulnerability in the transit-engine endpoint's ciphertextContainer.UnmarshalJSON function that fails to validate decoded ciphertext length before slicing. An authenticated workload with a valid mesh certificate can trigger a runtime panic by submitting a short base64-encoded ciphertext, causing log spam and request failures without crashing the process.

Record
GHSA-x66w-59x9-vvcf
CVSS 4.0
5.3 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L
Weakness
CWE-129
Updated
2026-11-02
NVD 5.3

Contrast through 1.20.0 contains a panic vulnerability in the transit-engine endpoint's ciphertextContainer.UnmarshalJSON function that fails to validate decoded ciphertext length before slicing. An authenticated workload with a valid mesh certificate can trigger a runtime panic by submitting a short base64-encoded ciphertext, causing log spam and request failures without crashing the process.

Record
CVE-2026-100836
CVSS 4.0
5.3 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L
Weakness
CWE-129
Updated
2026-11-02
OSV 5.3

Edgeless Systems Contrast through 1.20.0 Denial of Service via ciphertextContainer

Contrast through 1.20.0 contains a panic vulnerability in the transit-engine endpoint's ciphertextContainer.UnmarshalJSON function that fails to validate decoded ciphertext length before slicing. An authenticated workload with a valid mesh certificate can trigger a runtime panic by submitting a short base64-encoded ciphertext, causing log spam and request failures without crashing the process.

Record
CVE-2026-100836
CVSS 4.0
5.3 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N
Weakness
CWE-129
Updated
2026-11-02
OSVGHSA-3ccm-4qq2-5wrp 4.3

Constrata's coordinator transit engine `ciphertextContainer.UnmarshalJSON` panics on attacker-controlled short ciphertexts

Summary

ciphertextContainer.UnmarshalJSON decodes the third :-separated component of a vault:vX:base64... ciphertext and then unconditionally takes a 12-byte prefix slice for the AES-GCM nonce: c.nonce = fullCiphertext[:aesGCMNonceSize]. If the decoded blob is shorter than 12 bytes, the slice expression panics. The panic happens before any cryptographic operation, while the JSON body of the request is still being parsed inside the request handler. Because the handler is invoked from net/http's standard handler goroutine, the panic is recovered to a 500 response, but the request handler aborts mid-execution and the recovered panic appears in the Coordinator's logs. An authenticated workload that holds a valid mesh certificate for any WorkloadSecretID can trigger the panic at will, producing log spam, request-failure metrics, and a slow but cheap denial of service against the transit-engine endpoint.

Details

the panicking slice

coordinator/internal/transitengineapi/crypto.go:64-88:

// UnmarshalJSON umarshalls a json string to a ciphertextContainer holding the version prefix,
// decoded base64 nonce and ciphertext.
func (c *ciphertextContainer) UnmarshalJSON(data []byte) error {
	var encoded string
	if err := json.Unmarshal(data, &encoded); err != nil {
		return err
	}
	// Split "vault:vX:base64" format
	parts := strings.SplitN(encoded, ":", 3)
	if len(parts) < 3 {
		return fmt.Errorf("invalid ciphertext format")
	}
	version, err := extractVersion(parts[1])
	if err != nil {
		return fmt.Errorf("ciphertext version: %w", err)
	}
	c.keyVersion = version
	fullCiphertext, err := base64.StdEncoding.DecodeString(parts[2])
	if err != nil {
		return fmt.Errorf("decoding ciphertext: %w", err)
	}
	c.nonce = fullCiphertext[:aesGCMNonceSize]      // PANIC when len(fullCiphertext) < 12
	c.ciphertext = fullCiphertext[aesGCMNonceSize:]
	return nil
}

aesGCMNonceSize = 12 (defined at line 33). There is no length check on fullCiphertext. If parts[2] decodes to fewer than 12 bytes (which happens for any base64 string shorter than ~16 characters), the slice expression fullCiphertext[:aesGCMNonceSize] triggers Go's runtime panic runtime error: slice bounds out of range [:12] with length N.

UnmarshalJSON is reached from parseRequest:

// coordinator/internal/transitengineapi/transitengineapi.go:292-302
func parseRequest(r *http.Request, into any) error {
	defer r.Body.Close()
	if err := validateContentType(r); err != nil {
		return err
	}
	if err := json.NewDecoder(r.Body).Decode(into); err != nil {
		return err
	}
	return nil
}

which is called inside getDecryptHandler (line 178-237) before any other processing.

auth requirement is real but trivial to satisfy for any registered workload

The transit-engine HTTP server (transitengineapi.go:74-100) configures tls.RequireAndVerifyClientCert with the Coordinator's mesh CA pool. The handler is wrapped by authorizationMiddleware (line 348-357) which calls authorizeWorkloadSecret (line 241-254). That function reads the WorkloadSecretOID extension from the peer cert and requires it to match the URL path's {name} segment.

Any workload that has gone through the normal initializer / meshapi flow (coordinator/internal/meshapi/meshapi.go:71-119) and has a non-empty WorkloadSecretID in its PolicyEntry is issued a mesh cert with the matching extension, so the path-name authorisation is automatically satisfied for whichever workloadSecretID the manifest assigned to that workload. There is no rate limiting, no proof-of-work, and no audit log on triggering the panic.

what happens after the panic

net/http wraps each handler in a recovered goroutine, so the panic does not crash the Coordinator process. Instead:

  1. The Go runtime captures the panic, logs http: panic serving <peer>: runtime error: slice bounds out of range to stderr together with a goroutine stack trace.
  2. The connection is hung up without a response…
Record
GHSA-3ccm-4qq2-5wrp
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L
Weakness
CWE-129
Updated
2026-11-02
OSVGO-2026-5864 No score

Constrata's coordinator transit engine `ciphertextContainer.UnmarshalJSON` panics on attacker-controlled short ciphertexts in github.com/edgelesssys/contrast

Constrata's coordinator transit engine ciphertextContainer.UnmarshalJSON panics on attacker-controlled short ciphertexts in github.com/edgelesssys/contrast

Record
GO-2026-5864
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
github.com/edgelesssys/contrast
Go
through 1.20.0
1.21.0
github.com/edgelesssys/contrast
Go
before 1.21.0
1.21.0

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-100836 is a medium vulnerability (CVSS 5.3) in Edgelesssys Contrast, published July 1, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.18%, the 7th percentile. Fixed in github.com/edgelesssys/contrast 1.21.0.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open