Only the first Alt-Svc ALPN negotiation is verified
Categories
(Core :: Networking, defect, P1)
Tracking
()
People
(Reporter: pspaul95+bugzilla, Assigned: valentin)
References
Details
(Keywords: csectype-sop, reporter-external, sec-moderate, Whiteboard: [client-bounty-form][necko-triaged][necko-priority-queue][adv-main134+][adv-ESR128.6+])
Attachments
(7 files)
7.78 KB,
application/zip
|
Details | |
48 bytes,
text/x-phabricator-request
|
tjr
:
sec-approval+
|
Details | Review |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-beta+
|
Details | Review |
1.86 KB,
text/plain
|
Details | |
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-esr128+
|
Details | Review |
187 bytes,
text/plain
|
Details |
Version: Firefox 131.0.3 (64-bit)
OS: Manjaro Linux 24.1.1
How was this issue discovered?
Manual testing. I was researching cross-protocol attacks using the Alt-Svc header when I found this bypass.
General Description:
The Alt-Svc spec's section 2.4 (https://httpwg.org/specs/rfc7838.html#switching) mandates that the browser verifies that the alternative service negotiates the correct protocol, for example via ALPN:
If the connection to the alternative service does not negotiate the expected protocol (for example, ALPN fails to negotiate h2, or an Upgrade request to h2c is not accepted), the connection to the alternative service MUST be considered to have failed.
Firefox only verifies this for the first connection but not for subsequent ones. Therefore, a Man-in-the-Middle attacker can bypass the ALPN check by forwarding the first connection to a valid server, closing the connection after some time, and forwarding all subsequent requests to a different server.
The PoC demonstrates this by running three TLS servers, all using the same certificate:
- HTTP/1.1 on port
:8443
(ALPN offershttp1.1
) - HTTP/2 on port
:8444
(ALPN offersh2
) - Plain TLS on port
:8445
(ALPN list is empty)
Firefox should never use the alternative service h2=":8445"
because ALPN will not negotiate h2
. However, by forwarding the first request to :8444
, which correctly negotiates h2
, Firefox can be tricked into trusting all later connections.
Steps to reproduce:
- Start the PoC:
node server.js
- Add
127.0.0.1 foo.example.com
to your/etc/hosts
file - Visit
https://foo.example.com:8443/?alt-svc=:1337
in Firefox - Accept the self-signed certificate warning (or replace
privkey.pem
andfullchain.pem
with valid certificates before step 1) - Now the following should happen:
- The page will first be served via HTTP/1.1 over TLS from
:8443
, delivering theAlt-Svc: h2=":1337"
header - After 1 second, the page refreshes, causing the browser to make the first connection to the attacker's alternative service on
:1337
- The attacker forwards this first request to the legitimate HTTP/2 server on
:8444
- The new page loads, showing that it was loaded via HTTP/2
- The attacker now closes the forwarded connection between the browser and the HTTP/2 server
- The page then refreshes again, causing the browser to open a new connection to the attacker on
:1337
- The attacker now forwards the request to the invalid TLS server at
:8445
- The page loads, showing that it was loaded from
:8445
, which shouldn't happen
- The page will first be served via HTTP/1.1 over TLS from
The PoC script's output should demonstrate the same:
[h1] GET /?alt-svc=:1337
[attacker] Forwarding to h2 (:8444)
[h1] GET /favicon.ico
[h2] GET /?refresh
[attacker] Terminating first connection
[attacker] Forwarding to plain (:8445)
[plain] Got connection
[plain] Got data: GET / HTTP/1.1
[plain] Got data: Host: foo.example.com:8443
[plain] Got data: User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:131.0) Gecko/20100101 Firefox/131.0
...
I also recorded a demo of the PoC, I hope you enjoy it ;)
Root cause analysis:
I'm not very familiar with the Firefox code base, but it looks like TRRServiceChannel::BeginConnect()
is consulting the Alt-Svc cache here: https://searchfox.org/mozilla-central/rev/387f3edbef37d31b2e91fb0812c74b54729e86ff/netwerk/protocol/http/TRRServiceChannel.cpp#405-407
If there's a matching entry, it means that the alternative service was previously validated, and Firefox connects to the mConnectionInfo
from the cache entry instead of the original one.
I think the problem here is that the Alt-Svc cache holds the validation state. To me, the spec sounds like each new TLS connection needs to do the validation individually to prevent a Man-in-the-Middle attacker from switching the destination server at some point.
Comment 1•11 months ago
|
||
Looks like the attachment did not make it through yet. Submitting for pspaul.
Comment 2•11 months ago
|
||
Updated•11 months ago
|
Assignee | ||
Comment 3•11 months ago
|
||
Updated•11 months ago
|
Assignee | ||
Updated•11 months ago
|
Assignee | ||
Updated•11 months ago
|
Updated•11 months ago
|
Assignee | ||
Comment 4•11 months ago
|
||
Assignee | ||
Comment 5•11 months ago
|
||
Comment on attachment 9435935 [details]
Bug 1929156 - Check negotiated ALPN matches altSvc protocol r=#necko
Security Approval Request
- How easily could an exploit be constructed based on the patch?: Relatively easily.
- Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: Yes
- Which branches (beta, release, and/or ESR) are affected by this flaw, and do the release status flags reflect this affected/unaffected state correctly?: all
- If not all supported branches, which bug introduced the flaw?: None
- Do you have backports for the affected branches?: Yes
- If not, how different, hard to create, and risky will they be?: Applies cleanly to all branches
- How likely is this patch to cause regressions; how much testing does it need?: Unlikely to cause regressions.
- Is the patch ready to land after security approval is given?: Yes
- Is Android affected?: Yes
Comment 6•11 months ago
|
||
Do I understand correctly that exploiting this would require an unusual server configuration that is likely extremely uncommon?
Assignee | ||
Comment 7•11 months ago
|
||
(In reply to Tom Ritter [:tjr] from comment #6)
Do I understand correctly that exploiting this would require an unusual server configuration that is likely extremely uncommon?
Yes, the original server needs to provide an alt-svc directing the browser to a port it doesn't control. And even so, TLS is still required, so I'm not sure how much damage the attacker could do.
Comment 8•10 months ago
|
||
Comment on attachment 9435935 [details]
Bug 1929156 - Check negotiated ALPN matches altSvc protocol r=#necko
Approved to land and uplift
Updated•10 months ago
|
![]() |
||
Comment 10•10 months ago
|
||
Assignee | ||
Comment 11•10 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D228217
Updated•10 months ago
|
Updated•10 months ago
|
Comment 12•10 months ago
|
||
beta Uplift Approval Request
- User impact if declined: An attacker could make Firefox use a different protocol than the one specified in the ALPN header.
- Code covered by automated testing: yes
- Fix verified in Nightly: yes
- Needs manual QE test: yes
- Steps to reproduce for manual QE testing: see comment 0
- Risk associated with taking this patch: medium
- Explanation of risk level: Our code coverage for this isn't great. I'm not 100% sure our ALPN negotiation doesn't have any bugs that would cause us to fail loading websites in specific circumstances.
- String changes made/needed: none
- Is Android affected?: yes
Comment 13•10 months ago
|
||
I misunderstood this as a certificate check bypass. Lowering to sec-moderate
Updated•10 months ago
|
Updated•10 months ago
|
Updated•10 months ago
|
Comment 14•10 months ago
|
||
uplift |
Updated•10 months ago
|
Comment 15•10 months ago
|
||
I was able to reproduce the issue on Firefox 131.0.3 using Windows 11, macOS 13.6 and Ubuntu 22.04, as described in Comment 0.
Verified this with Firefox Nightly 135.0a1 (2024-12-05) and with Firefox 134.0b7 (treeherder build from Comment 14) on the previously mentioned OSes and after accessing https://foo.example.com:8443/?alt-svc=:1337, the browser traffic is redirected as follows:
- from ALPN=http/1.1 (:8443) to ALPN=h2 (:8444) and then back to ALPN=http/1.1 (:8443).
Regarding the terminal/cmd output, we noticed that when the attacker executes "[attacker] Forwarding to plain (:8445)", the process is automatically interrupted on the fixed builds:
- either the server shuts down (on macOS/Windows)
- or logs stop appearing (on Ubuntu).
I will attach a file with the script output. @valentin, could you please take a look at the output and let us know if the results are as expected? Thank you in advance!
Assignee | ||
Comment 16•10 months ago
|
||
Thank you, Bianca. That is the expected behaviour.
Comment 17•10 months ago
|
||
Thank you for the confirmation!
Based on Comment 15 and 16 I am marking this verified as fixed on the previously mentioned versions.
Comment 18•10 months ago
|
||
Please nominate this for ESR128 approval also.
Assignee | ||
Comment 19•10 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D228217
Updated•10 months ago
|
Comment 20•10 months ago
|
||
esr128 Uplift Approval Request
- User impact if declined: An attacker could make Firefox use a different protocol than the one specified in the ALPN header.
- Code covered by automated testing: yes
- Fix verified in Nightly: yes
- Needs manual QE test: yes
- Steps to reproduce for manual QE testing: see comment 0
- Risk associated with taking this patch: medium
- Explanation of risk level: Our code coverage for this isn't great. I'm not 100% sure our ALPN negotiation doesn't have any bugs that would cause us to fail loading websites in specific circumstances.
- String changes made/needed: none
- Is Android affected?: yes
Updated•10 months ago
|
Comment 21•10 months ago
|
||
uplift |
Updated•10 months ago
|
Assignee | ||
Updated•10 months ago
|
Comment 22•10 months ago
|
||
Verified as fixed on Firefox 128.6.0esr, build ID 20241213173517 (treeherder build from Comment 21), using Windows 11, macOS 13.6 and Ubuntu 22.04.
Updated•9 months ago
|
Comment 23•9 months ago
|
||
Updated•9 months ago
|
Reporter | ||
Comment 24•8 months ago
|
||
Hey, thanks for taking care of this bug and thanks for the bounty!
I would like to publish a blog post about this bug, is there any embargo period I have to take into account or could I publish it right away?
Thanks!
Comment 25•8 months ago
|
||
This was fixed last release (~4 weeks ago). We generally wait about six weeks to make sure that even users in the more remote parts of the world had the opportunity to upgrade. If possible, I would like you to wait you for another two weeks.
Reporter | ||
Comment 26•8 months ago
|
||
Sounds good to me! I'll publish it in two weeks then.
Updated•4 months ago
|
Description
•