refraction-networking / utls

Fork of the Go standard TLS library, providing low-level access to the ClientHello for mimicry purposes.
BSD 3-Clause "New" or "Revised" License
1.57k stars 231 forks source link

HelloFirefox* gets an ECDSA verification failure #274

Open danielboros opened 6 months ago

danielboros commented 6 months ago

Seems to happen on facebook.com as well as fbcdn.net. Minimal recreate below.

package main

import (
    "fmt"
    "net"
    "net/http"
    "strings"
    "time"

    utls "github.com/refraction-networking/utls"
    "golang.org/x/net/http2"
)

func main() {
    hostWithoutPort := "facebook.com"
    ips, err := net.LookupIP(hostWithoutPort)
    if err != nil {
        panic(err)
    }
    var ip string
    if len(ips) > 0 {
        ip = ips[0].String()
        if strings.Contains(ip, ":") {
            ip = fmt.Sprintf("[%s]", ip)
        }
    } // IPv6

    config := utls.Config{
        ServerName: hostWithoutPort,

        // TODO: Possibly remove InsecureSkipVerify
        InsecureSkipVerify: true,
    }
    dialConn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", ip, "443"), 10*time.Second)
    if err != nil {
        panic(err)
    }
    utlsConn := utls.UClient(dialConn, &config, utls.HelloFirefox_Auto)

    err = utlsConn.Handshake()
    if err != nil {
        panic(err)
    }

    h2 := &http2.Transport{}
    conn, err := h2.NewClientConn(utlsConn)
    if err != nil {
        panic(err)
    }

    req, err := http.NewRequest(http.MethodGet, "https://facebook.com", nil)
    if err != nil {
        panic(err)
    }

    resp, err := conn.RoundTrip(req)
    if err != nil {
        panic(err)
    }
    fmt.Printf("resp: %+v\n", resp)
}

Changing utls.HelloFirefox_Auto to utls.HelloChrome_Auto appears to resolve the issue.

gaukas commented 6 months ago

It is a known issue caused by TLS extension delegated_credentials (34) (draft-ietf-tls-subcerts-15). Firefox parrots in uTLS advertise this extension by FakeDelegatedCredentialsExtension without implementing any support of it, which causes issue if the server selected it and proceeded.

While adding support to delegated_credentials is not currently planned, we welcome any community contribution in implementing ANY currently unsupported extensions. FYI, cloudflare/go has implemented it which could be a good reference.

danielboros commented 6 months ago

Sounds like I will take on this adventure. Will update here when I make some progress.

jjsaunier commented 6 months ago

The only issue is that we are required to implement the delegated credential extension in the x509 parser as well

@gaukas what do you suggest? a local copy like dicttls ?

gaukas commented 6 months ago

Hmm. That's pretty tough.

I am not super familiar with this extension yet, but from cloudflare/go I can see it is due to our certificate handling (from crypto/tls) is directly using the x509 package which did not support some specific functionality for this extension or something.

Not an expert in this but is it possible for us to override a standard library to a remote package (say cloudflare/go/crypto/x509)? I personally don't believe vendor-ing a mission critical package would bring us more benefits than trouble, at least in the long run.

I would rather suggest instead trying persuading go team to accept the required change and backport it into currently maintained versions. But afterall, if we really want to vendor it, we might want to minimize the impact by strictly limiting the use of our x509 which would go out-of-date very easily.