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.67k stars 237 forks source link

[Question] How to capture TLS clienthello handshake raw data on a server? #200

Closed 1366613 closed 1 year ago

1366613 commented 1 year ago

I need to capture the client hello message in a server context to generate a ja3 hash from it. Though I'm currently using gopacket, things are a bit messy. As far as I see, the vanilla crypto/tls misses one key piece of data in order to generate a ja3 hash. Is it possible with utls?

gaukas commented 1 year ago

To capture TLS clienthello handshake raw data on a server, I am assuming you mean to capture the raw packet/bytes sent on wire.

I am not understanding what is your use case so here are some general advices.

Despite tcpdump/wireshark and other pcap tools, if you want to process/handle your ClientHello in real-time, you may go with the approach of tlsfingerprint.io, which is powered by clienthellod (demo).

gaukas commented 1 year ago

Just to clarify, refraction's TLS suite (utls, tlsfingerprint.io, clienthellod) DOES NOT use or provide support for JA3 hashes, which means you will have to implement it for your own use cases.

fedosgad commented 1 year ago

Sorry for comment on closed issue. If you want to get raw ClientHello bytes inside your golang application, you can take a look at https://github.com/fedosgad/mirror_proxy/blob/master/hijackers/utls.go (disclaimer: I'm the author). This is exactly what is done in the code in order to mimic incoming connection's TLS fingerprint.

1366613 commented 1 year ago

@fedosgad Doesn't it use any C bindings anywhere in the chain? Can this be used in the context of a custom net listener?

fedosgad commented 1 year ago

@aurora1369 No C bindings are used. If you mean context.Context, then I'm not sure (AFAIK stdlib's net.Listener methods usually do not accept context.Context). Otherwise - yes (it is actually the way I did it).

7c commented 7 months ago

@fedosgad Doesn't it use any C bindings anywhere in the chain? Can this be used in the context of a custom net listener?

you can use gopacket/pcap

handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever)
var filter string = "tcp and port 443"
    if err := handle.SetBPFFilter(filter); err != nil {
        log.Fatal(err)
    }
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {
tcpLayer := packet.Layer(layers.LayerTypeTCP)
        if tcpLayer == nil {
            panic("TCP layer not found")
        }
tcp, _ := tcpLayer.(*layers.TCP)
}

then look for first tcp.Payload which has 0x16 as first byte and send it ReadClientHello: ch2, err := clienthellod.ReadClientHello(bytes.NewReader(tcp.Payload))