Re: [OAUTH-WG] Edge case in RFC 7636, Server Verifies code_verifier facilitates Login-CSRF

Christopher Burroughs <chris.burroughs@protonmail.ch> Wed, 05 January 2022 14:56 UTC

Return-Path: <chris.burroughs@protonmail.ch>
X-Original-To: oauth@ietfa.amsl.com
Delivered-To: oauth@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id 316EF3A0B35 for <oauth@ietfa.amsl.com>; Wed, 5 Jan 2022 06:56:50 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -0.753
X-Spam-Level:
X-Spam-Status: No, score=-0.753 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, HTML_FONT_LOW_CONTRAST=0.001, HTML_MESSAGE=0.001, NUMERIC_HTTP_ADDR=1.242, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001, URI_HEX=0.1] autolearn=ham autolearn_force=no
Authentication-Results: ietfa.amsl.com (amavisd-new); dkim=pass (2048-bit key) header.d=protonmail.ch
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id kdes0yW6CF4p for <oauth@ietfa.amsl.com>; Wed, 5 Jan 2022 06:56:45 -0800 (PST)
Received: from mail-40133.protonmail.ch (mail-40133.protonmail.ch [185.70.40.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id B086C3A0B31 for <oauth@ietf.org>; Wed, 5 Jan 2022 06:56:44 -0800 (PST)
Date: Wed, 05 Jan 2022 14:56:01 +0000
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.ch; s=protonmail2; t=1641394599; bh=nGInPhhIVoLONWDUR04noWrqxy14F5WS9b2pFY5y0lY=; h=Date:To:From:Cc:Reply-To:Subject:Message-ID:In-Reply-To: References:From:To:Cc; b=RQa31tC56g4uyNf6hbr0Gnmx5yMXLWgBOkXc38c6Fl5Tpedz4RlpyEAswgIGvtIro gvDHRv55PTtfx7Op3HZwKcBZwBdft9F0DxFAsLxcG+BX+y/vXIdc6mgzyemDkFr8jc iIT5oDhWl+j+W2gy1TnrDpY3ZG5MYrZhLYYTVQLyfHVogO84RXHQjX4O4xwHD1zoz2 twSzSBO6aU5i6Goiv9F2GDMe5glpCFVZxXtRSLOa0F1/w2wFCEQCwz8rBDR5QyvwCo FZfSUjNeAdYmoDTc0L6inDZX7frd9PzsHuFc1hlXYB671sPD2GIOebPreEPevfReCC 1TRiCUYi4DqKQ==
To: Benjamin.Haeublein@cirosec.de, wparad@rhosys.ch
From: Christopher Burroughs <chris.burroughs@protonmail.ch>
Cc: oauth@ietf.org
Reply-To: Christopher Burroughs <chris.burroughs@protonmail.ch>
Message-ID: <6Kxw1IhJ0CB5svSOaCq1pBJLwZcQNvswr1Uw4mq8xswG9Z8fDMMevvE95zYV8WxcT1eLriglIcDv1hPr3j-d5GQ0ByLae9zC2HM8SVmoHUI=@protonmail.ch>
In-Reply-To: <AM7P192MB0819D3D6523981A8495D5CD7E44B9@AM7P192MB0819.EURP192.PROD.OUTLOOK.COM>
References: <AM7P192MB0819568B6CF315C3BC3A9B15E44A9@AM7P192MB0819.EURP192.PROD.OUTLOOK.COM> <b711bc01-9674-6072-d941-7e1ba8f8c9ec@aol.com> <AM7P192MB081909F590F651D656ACC892E44B9@AM7P192MB0819.EURP192.PROD.OUTLOOK.COM> <CAJot-L2o32DCswBAoq6DKZgZmEKgafEk=_AGnj5y3q3CBPsa3A@mail.gmail.com> <AM7P192MB0819D3D6523981A8495D5CD7E44B9@AM7P192MB0819.EURP192.PROD.OUTLOOK.COM>
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="b1_pnKdi5n13whRas1v1zqTk8mBY3wOlHCjEbUp23yQo"
Archived-At: <https://mailarchive.ietf.org/arch/msg/oauth/OXd8W-JqysY5nrqZkiKv5KWUoUs>
Subject: Re: [OAUTH-WG] Edge case in RFC 7636, Server Verifies code_verifier facilitates Login-CSRF
X-BeenThere: oauth@ietf.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: OAUTH WG <oauth.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/oauth>, <mailto:oauth-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/oauth/>
List-Post: <mailto:oauth@ietf.org>
List-Help: <mailto:oauth-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/oauth>, <mailto:oauth-request@ietf.org?subject=subscribe>
X-List-Received-Date: Wed, 05 Jan 2022 14:56:50 -0000

Greetings,

Is this scenario any different from a PKCE downgrade attack, as described in OAuth 2.0 Security Best Current Practice section 4.8.2 ?

Warm regards and happy new year!

Christopher Burroughs

-------- Original Message --------
On Jan 5, 2022, 21:29, Benjamin Häublein wrote:

> The following example shows this behavior in keycloak:
>
> Authorization Request:
>
> http://identity-provider:8080/auth/realms/XXX/protocol/openid-connect/auth?client_id=client-spa-public-pkce&redirect_uri=http%3A%2F%2Flocalhost%2F&response_mode=fragment&response_type=code&scope=openid
>
> Authorization Response:
>
> http://127.0.0.1/#session_state=46556363-c53f-489f-871c-58d376a8f9c1&code=2b84ee14-68ff-48c9-b480-4349ff9805f7.46556363-c53f-489f-871c-58d376a8f9c1.6fab0727-6184-47ad-8607-55f19896e945
>
> Token Request:
>
> POST /auth/realms/XXX/protocol/openid-connect/token HTTP/1.1
> Host: identity-provider:8080
> Content-type: application/x-www-form-urlencoded
>
> code=2b84ee14-68ff-48c9-b480-4349ff9805f7.46556363-c53f-489f-871c-58d376a8f9c1.6fab0727-6184-47ad-8607-55f19896e945&grant_type=authorization_code&client_id=client-spa-public-pkce&redirect_uri=http%3A%2F%2Flocalhost%2F&code_verifier=IqiCQGM06JEyW73AB3f3oblCQKHOorapyqHUcYRujuSikDJx8cvBQ0kmFmzW75uIfaSBtXQrRmwuk71WWO6ryCzahTcxBPYX
>
> As a result, an access token was issued although the code_verifier provided in the token request did not match the code_challenge and code_challenge_method in the authorization request as they were absent.
>
> An attacker can thus send the victim the authorization response, after the victim clicks the link, the client application sends a Token Request with the code_verifier present with the client to Keycloak.
>
> As a result, a token is issued for the application, although the code_verifier does not match the inexistent code_challenge/code_challenge_method in the malicious authorization response.
>
> For this to work, the client must either generate a code_verifier on the fly or have one already present. This obviously depends on the precise implementation of the respective client.
>
> To reach such a state, an attacker could trick the user into starting the authorization grant but clicking the malicious link before the authorization response is sent.
>
> Best Regards,
>
> Benjamin Häublein
> Senior Consultant
>
> cirosec GmbH
> Ferdinand-Braun-Strasse 4
> 74074 Heilbronn
> Germany
> Phone: +49 (7131) 59455-74
> Fax: +49 (7131) 59455-99
> Mobile: +49 (151) 122414-74www.cirosec.de
>
> HRB Stuttgart 107883
> CEO Stefan Strobel, CFO Peter Lips
>
> Von: Warren Parad <wparad@rhosys.ch>
> Gesendet: Mittwoch, 5. Januar 2022 13:44
> An: Benjamin Häublein <Benjamin.Haeublein@cirosec.de>
> Cc: George Fletcher <gffletch@aol.com>; oauth@ietf.org
> Betreff: Re: [OAUTH-WG] Edge case in RFC 7636, Server Verifies code_verifier facilitates Login-CSRF
>
> Sie erhalten nicht oft E-Mail von "wparad@rhosys.ch". [Weitere Informationen, warum dies wichtig ist](http://aka.ms/LearnAboutSenderIdentification)
>
> I'm not following to be honest. Could you detail concretely what the flow would be that would result in vulnerability?
>
> Warren Parad
>
> Founder, CTO
>
> Secure your user data with IAM authorization as a service. Implement [Authress](https://authress.io/).
>
> On Wed, Jan 5, 2022 at 1:41 PM Benjamin Häublein <Benjamin.Haeublein@cirosec.de> wrote:
>
>> Finally, I'm not sure a client that doesn't send the 'code_challenge' and 'code_challenge_method' on the authorization request but does send the 'code_verifier' on the token request should consider that the client has implemented PKCE correctly and hence can rely on it for CSRF.
>>
>> My point is not, that a client behaves that way. The problem is that an attacker could get a user (through social engineering) to start the authorization process and then click a link with an authorization response that the attacker provides.
>>
>> Then the client has send the 'code_challenge' and 'code_challenge_method' in the authorization request, but the authorization response belongs to an authorization request that does not have these parameters.
>>
>> When the client sends the token request based on the malicious authorization request but with the ‘code_verifier’ for the original authorization request.
>>
>> When the AS behaves as described the client has no way to know that an attacker has interfered.
>>
>> Best Regards,
>>
>> Benjamin Häublein
>>
>> Von: George Fletcher <gffletch@aol.com>
>> Gesendet: Dienstag, 4. Januar 2022 14:51
>> An: Benjamin Häublein <Benjamin.Haeublein@cirosec.de>; oauth@ietf.org
>> Betreff: Re: [OAUTH-WG] Edge case in RFC 7636, Server Verifies code_verifier facilitates Login-CSRF
>>
>> My guess is that for an Authorization Server that doesn't receive a 'code_challenge' and 'code_challenge_method' as part of the authorization request, they treat the request as a non-PKCE authorization request. Therefore when the 'code_verifier' is presented at the /token endpoint, the AS ignores the parameter because it doesn't consider the request to be a PKCE request. I can also see the AS returning an error regarding an "unexpected parameter" or "invalid request" error in this case.
>>
>> I agree with your recommendation for the AS to require specific clients to use PKCE and consider an authorization request without PKCE to be an error.
>>
>> Finally, I'm not sure a client that doesn't send the 'code_challenge' and 'code_challenge_method' on the authorization request but does send the 'code_verifier' on the token request should consider that the client has implemented PKCE correctly and hence can rely on it for CSRF.
>>
>> Thanks,
>> George
>>
>> On 1/4/22 5:45 AM, Benjamin Häublein wrote:
>>
>>> Hello everyone,
>>>
>>> I think RFC 7636 “Proof Key for Code Exchange by OAuth Public Clients”, section 4.6. “Server Verifies code_verifier before Returning the Tokens” leaves a tiny gap regarding the handling of verification when no code challenge was present in the authorization request:
>>>
>>> Upon receipt of the request at the token endpoint, the server
>>>
>>> verifies it by calculating the code challenge from the received
>>>
>>> "code_verifier" and comparing it with the previously associated
>>>
>>> "code_challenge", after first transforming it according to the
>>>
>>> "code_challenge_method" method specified by the client.
>>>
>>> It is unspecified how the server should behave when “code_verifier” is present, but “code_challenge” and “code_challenge_method” were not set in the initial authorization request.
>>>
>>> The following example worked for three well-known authorization servers where the client was configured in a way that PKCE could be used, but was not enforced:
>>>
>>> Authorization Request:
>>>
>>> https://XXXX/auth?client_id=YYYY&response_type=code&scope=openid+profile&redirect_uri=https://localhost
>>>
>>> Subsequent Token Request:
>>>
>>> POST /token HTTP/1.1
>>> Host: XXXX
>>> Content-Length: 256
>>>
>>> code=ZZZZ&grant_type=authorization_code&client_id=YYYY&redirect_uri=https%3A%2F%2Flocalhost&code_verifier=IqiCQGM06JEyW73AB3f3oblCQKHOorapyqHUcYRujuSikDJx8cvBQ0kmFmzW75uIfaSBtXQrRmwuk71WWO6ryCzahTcxBPYX
>>>
>>> As a result, an access token was issued although the code_verifier provided in the token request did not match the code_challenge and code_challenge_method in the authorization request.
>>>
>>> Many applications consider the usage of PKCE as enough protection from Login-CSRF and do not use state or nonce (for example this blog entry by Daniel Fett https://danielfett.de/2020/05/16/pkce-vs-nonce-equivalent-or-not/ suggests, that neither state nor nonce are necessary when PKCE is used). However, when the authorization server is not configured to require a specific code_challenge_method from the client and the authorization behaves as described in the example, PKCE does not protect from Login-CSRF.
>>>
>>> [I think the following mitigations are possible:]
>>>
>>> -  Enforce usage of PKCE in the client configuration in the Authorization Server
>>>
>>> -  Implementation of the authorization server returns an error in the Access Token Response when code_verifier is present in the token request, but no code_challenge and code_challenge_method is present in the authorization request.
>>>
>>> -  Additionally, when the behavior of an AS is correct (verification of code_verifier fails when no code_challenge was present), a client that relies on PKCE for CSRF protection must always include a code_verifier parameter in the token request (even if no code_verifier is present on the client side).
>>>
>>> Best regards,
>>>
>>> Benjamin Häublein
>>> Senior Consultant
>>>
>>> cirosec GmbH
>>> Ferdinand-Braun-Strasse 4
>>> 74074 Heilbronn
>>> Germany
>>> Phone: +49 (7131) 59455-74
>>> Fax: +49 (7131) 59455-99
>>> Mobile: +49 (151) 122414-74
>>> www.cirosec.de
>>>
>>> HRB Stuttgart 107883
>>> CEO Stefan Strobel, CFO Peter Lips
>>>
>>> _______________________________________________
>>>
>>> OAuth mailing list
>>>
>>> OAuth@ietf.org
>>>
>>> https://www.ietf.org/mailman/listinfo/oauth
>>
>> _______________________________________________
>> OAuth mailing list
>> OAuth@ietf.org
>> https://www.ietf.org/mailman/listinfo/oauth