Hi,

Not sure if you would call this a security issue, hence I am asking this on the 
mailing list prior to opening a github issue:

I’ve noticed that it is really easy to bypass the check on client certificates 
of a domain when the client can present a valid certificate for another domain.

Consider this HAproxy config:

global
  log /dev/log len 4096 format rfc3164 syslog info

defaults
  log global
  mode http
  timeout connect 5s
  timeout client 1h
  timeout server 5s

frontend myfrontend
  bind :443 ssl crt /etc/cert/server.pem crt-list /crt-list
  log-format "%ci:%cp [%tr] (%ID) %ft %b/%s %TR/%Tw/%Tc/%Tr/%Tt %ST %B %CC %CS 
%tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r %sslc %sslv"

  capture request header Host len 256

  http-request set-header X-SSL-Client              %[ssl_c_used]            if 
{ ssl_c_used }
  http-request set-header X-SSL-Client-Session-ID   %[ssl_fc_session_id,hex] if 
{ ssl_c_used }
  http-request set-header X-SSL-Client-Verify       %[ssl_c_verify]          if 
{ ssl_c_used }
  http-request set-header X-SSL-Client-Subject-DN   %{+Q}[ssl_c_s_dn]        if 
{ ssl_c_used }
  http-request set-header X-SSL-Client-Subject-CN   %{+Q}[ssl_c_s_dn(cn)]    if 
{ ssl_c_used }
  http-request set-header X-SSL-Client-Issuer-DN    %{+Q}[ssl_c_i_dn]        if 
{ ssl_c_used }
  http-request set-header X-SSL-Client-NotBefore    %{+Q}[ssl_c_notbefore]   if 
{ ssl_c_used }
  http-request set-header X-SSL-Client-NotAfter     %{+Q}[ssl_c_notafter]    if 
{ ssl_c_used }

  use_backend bob if { hdr(host) -m dom bob.com }
  use_backend alice if { hdr(host) -m dom alice.com }

  default_backend alice

backend alice
  server alice localhost:8080 check

backend bob
  server bob localhost:8081 check

---
So this HAproxy hosts two domains alice.com and bob.com. It uses the following 
crt-list to make TLS connections:

/etc/cert/server.pem [ca-file /alice.ca.pem verify required] *.alice.com
/etc/cert/server.pem [ca-file /bob.ca.pem verify required] *.bob.com

---
So any client connecting to alice.com must present a certificate signed by the 
Alice CA and any client connecting to bob.com must present a certificate signed 
by the Bob CA.


However, I noticed that HAproxy does allow me to “spoof” the host header to 
bob.com even though I did a TLS handshake with alice.com. The request will be 
forwarded to bob.com with the alice.com certificate:

curl -v -k --cert alice.com.crt --key alice.com.key --resolve 
www.alice.com:9443:127.0.0.1<http://www.alice.com:9443:127.0.0.1> 
https://www.alice.com:9443/headers -H "host: www.bob.com<http://www.bob.com>"

* Added www.alice.com:9443:127.0.0.1 to DNS cache
* Hostname www.alice.com was found in DNS cache
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to www.alice.com (127.0.0.1) port 9443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
(…)
> GET /headers HTTP/1.1
> Host: www.bob.com
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< date: Thu, 24 Jun 2021 13:07:17 GMT
< content-length: 578
< content-type: text/plain; charset=utf-8
<
Hello from Bob!

----- Headers Received -----
Accept : [*/*]
User-Agent : [curl/7.64.1]
X-Ssl-Client : [1]
X-Ssl-Client-Issuer-Dn : 
[/C=US/ST=California/L=AliceLand/O=Alice.com/CN=Alice.com Root 
CA/[email protected]]
X-Ssl-Client-Notafter : [220624125634Z]
X-Ssl-Client-Notbefore : [210624125634Z]
X-Ssl-Client-Session-Id : 
[D941ECCAACAFEBC5CB3AE17794B54DC3DFC7549C401DB20D7EC5ADC48244D3D0]
X-Ssl-Client-Subject-Cn : [Alice]
X-Ssl-Client-Subject-Dn : 
[/C=US/ST=Michigan/L=Detroit/O=Alice.com/CN=Alice/[email protected]]
X-Ssl-Client-Verify : [0]

---
So basically anyone who can get a client certificate from Alice.com can use it 
to also connect to Bob.com without getting validated against Bob’s CA.

I’ve tested this with HAproxy 2.2.14.

My questions:

  *   HAproxy does seem to treat SNI (L5) and HTTP Host Header (L7) as 
unrelated. Is this true?
  *   Applications offloading TLS to HAproxy usually trust that mTLS requests 
coming in are validated correctly. They usually don’t revalidate the entire 
certificate again and only check for the subject’s identity. Is there a way to 
make SNI vs host header checking more strict?
  *   What’s the best practice to dispatch mTLS requests to backends? I’ve used 
a host header based approach here but it shows the above vulnerabilities.


Best regards,
Dom

Reply via email to