Skip to content

EAP-GTC is not restricted to protected contexts and can transmit credentials in cleartext #148

Description

@code-and-covfefe

TRANSPARENCY: This is an AI-assisted submission – Aikido

Summary
gtc.Payload.Offerable() always returns true, so the generic protocol-selection handler can select EAP-GTC in an unprotected root context or as a configured fallback. GTC then emits its challenge and accepts the peer response as ordinary EAP payload data, without a confidentiality-context or downgrade-policy check.

Description
EAP-GTC is a credential-bearing method whose response is only suitable for transmission when encapsulated by an authenticated, confidentiality-providing method such as PEAP, TTLS, or TLS. This implementation does not encode that requirement. Offerable() unconditionally authorizes GTC, and the generic handler uses that result for protocol selection without distinguishing root and protected inner contexts. Encode() returns the configured challenge directly and Decode() retains the peer response directly, so a root-level or fallback GTC exchange exposes the credential-bearing response on the EAP wire. The handler also advances through configured protocols after a NAK, allowing GTC to be selected as an insecure fallback when it appears in the configured list. The example uses GTC as an inner method, but the public protocol configuration can also make it available at the root level. This is a real security issue when the application configuration permits that composition: an observer of the client-facing EAP path can recover passwords, OTPs, or bearer tokens, and a downgrade path can defeat an administrator's stronger-method preference.

Risk
Medium: the attacker needs observation or interception access to the client-facing EAP path, and the deployment must configure GTC as a root method or permit it as a fallback. Once selected, this method applies no cryptographic protection to the credential-bearing EAP payload.

Evidence
1
GTC is unconditionally marked offerable, with no requirement that it run inside a protected tunnel.

func (p *Payload) Offerable() bool {
	return true
}

2
The generic handler relies only on Offerable() before offering the configured protocol and does not apply a root-versus-inner confidentiality policy.

np, t, _ := eap.EmptyPayload(stm.GetEAPSettings(), nextChallengeToOffer)
...
if !np.Offerable() {
	ctx.Log().Debug("Root-EAP: protocol not offerable, skipping")
	return next()
}

3
The peer response is retained verbatim and the configured challenge is emitted verbatim, making both ordinary EAP payload bytes rather than data protected by GTC itself.

func (p *Payload) Decode(raw []byte) error {
	p.raw = raw
	return nil
}

func (p *Payload) Encode() ([]byte, error) {
	return p.Challenge, nil
}

4
The unprotected raw response is passed directly to the application validator and can complete authentication.

st := settings.ValidateResponse(ctx, p.raw)
if st != protocol.StatusUnknown {
	ctx.EndInnerProtocol(st)
	return &Payload{}
}

5
After a non-initial NAK, the handler advances to the next configured method without checking whether that fallback is an unacceptable security downgrade.

l.Debug("Root-EAP: received NAK, trying next protocol", "desired", n.DesiredType)
pp.(*eap.Payload).Payload = nil
return next()

6
The example demonstrates inner use, while the same public settings structure can place GTC in a root protocol list without a library-level restriction.

InnerProtocols: protocol.Settings{
	Protocols: []protocol.ProtocolConstructor{
		identity.Protocol,
		legacy_nak.Protocol,
		gtc.Protocol,
		mschapv2.Protocol,
	},
	ProtocolPriority: []protocol.Type{mschapv2.TypeMSCHAPv2, gtc.TypeGTC},

Root Cause Analysis
The handler calls np.Offerable() before offering a configured protocol and does not otherwise establish that the current context is a protected inner tunnel. Because GTC's implementation always returns true, a root-level configuration containing GTC reaches its normal handling path. Its challenge is returned as ordinary EAP application data, and the peer's raw response is passed to ValidateResponse without cryptographic protection supplied by this method. Separately, the NAK path advances to the next configured protocol without checking whether the next method is an unacceptable security downgrade. Thus the code exposes a credential-bearing cleartext method whenever application configuration permits GTC outside a confidentiality-providing context.

Remediation

Require an authenticated confidentiality-providing parent context before GTC is offerable, and enforce the requirement at the protocol-selection boundary rather than relying solely on caller configuration. Reject GTC in root-level protocol lists by default, or require an explicit insecure-use policy for that mode. Also prevent NAK-driven fallback from selecting GTC unless the protected-context or explicit-policy check succeeds. Preserve GTC for validated PEAP/TTLS/TLS inner contexts.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions