diff --git a/example.config.toml b/example.config.toml index 618ec19..7402a57 100644 --- a/example.config.toml +++ b/example.config.toml @@ -13,7 +13,7 @@ # Debug starts application in debug mode. It starts to be quite verbose # in output. Actually, the idea is that you run it in debug mode only if # you have any issue. -debug = false +debug = true # A secret. Please remember that mtg supports only FakeTLS mode, legacy # simple and secured mode are prohibited. For you it means that secret diff --git a/mtglib/internal/faketls/client_hello.go b/mtglib/internal/faketls/client_hello.go index b32eea0..62d3278 100644 --- a/mtglib/internal/faketls/client_hello.go +++ b/mtglib/internal/faketls/client_hello.go @@ -14,6 +14,7 @@ type ClientHello struct { Time time.Time Random [RandomLen]byte SessionID []byte + Host string CipherSuite uint16 } @@ -28,6 +29,15 @@ func ParseClientHello(secret, handshake []byte) (ClientHello, error) { return hello, fmt.Errorf("unknown handshake type %#x", handshake[0]) } + handshakeSizeBytes := [4]byte{0, handshake[1], handshake[2], handshake[3]} + handshakeLength := binary.BigEndian.Uint32(handshakeSizeBytes[:]) + + if len(handshake)-4 != int(handshakeLength) { + return hello, + fmt.Errorf("incorrect handshake size. manifested=%d, real=%d", + handshakeLength, len(handshake)-4) // nolint: gomnd + } + copy(hello.Random[:], handshake[ClientHelloRandomOffset:]) for i := ClientHelloRandomOffset; i < ClientHelloRandomOffset+RandomLen; i++ { @@ -61,11 +71,48 @@ func ParseClientHello(secret, handshake []byte) (ClientHello, error) { timestamp := int64(binary.LittleEndian.Uint32(computedRandom[RandomLen-4:])) hello.Time = time.Unix(timestamp, 0) - hello.SessionID = make([]byte, handshake[ClientHelloSessionIDOffset]) - copy(hello.SessionID, handshake[ClientHelloSessionIDOffset+1:]) - - cipherSuiteOffset := ClientHelloSessionIDOffset + len(hello.SessionID) + 3 // nolint: gomnd - hello.CipherSuite = binary.BigEndian.Uint16(handshake[cipherSuiteOffset : cipherSuiteOffset+2]) + parseSessionID(&hello, handshake) + parseCipherSuite(&hello, handshake) + parseSNI(&hello, handshake) return hello, nil } + +func parseSessionID(hello *ClientHello, handshake []byte) { + hello.SessionID = make([]byte, handshake[ClientHelloSessionIDOffset]) + copy(hello.SessionID, handshake[ClientHelloSessionIDOffset+1:]) +} + +func parseCipherSuite(hello *ClientHello, handshake []byte) { + cipherSuiteOffset := ClientHelloSessionIDOffset + len(hello.SessionID) + 3 // nolint: gomnd + hello.CipherSuite = binary.BigEndian.Uint16(handshake[cipherSuiteOffset : cipherSuiteOffset+2]) +} + +func parseSNI(hello *ClientHello, handshake []byte) { + cipherSuiteOffset := ClientHelloSessionIDOffset + len(hello.SessionID) + 1 + handshake = handshake[cipherSuiteOffset:] + + cipherSuiteLength := binary.BigEndian.Uint16(handshake[:2]) + handshake = handshake[2+cipherSuiteLength:] + + compressionMethodsLength := int(handshake[0]) + handshake = handshake[1+compressionMethodsLength:] + + extensionsLength := binary.BigEndian.Uint16(handshake[:2]) + handshake = handshake[2 : 2+extensionsLength] + + for len(handshake) > 0 { + if binary.BigEndian.Uint16(handshake[:2]) != ExtensionSNI { + extensionsLength := binary.BigEndian.Uint16(handshake[2:4]) + handshake = handshake[4+extensionsLength:] + + continue + } + + hostnameLength := binary.BigEndian.Uint16(handshake[7:9]) + handshake = handshake[9:] + hello.Host = string(handshake[:int(hostnameLength)]) + + return + } +} diff --git a/mtglib/internal/faketls/init.go b/mtglib/internal/faketls/init.go index 54d1186..dfa269b 100644 --- a/mtglib/internal/faketls/init.go +++ b/mtglib/internal/faketls/init.go @@ -7,7 +7,7 @@ const ( ClientHelloRandomOffset = 6 ClientHelloSessionIDOffset = ClientHelloRandomOffset + RandomLen - ClientHelloMinLen = ClientHelloSessionIDOffset + 1 + ClientHelloMinLen = 4 WelcomePacketRandomOffset = 11 @@ -15,6 +15,8 @@ const ( HandshakeTypeServer = 0x02 ChangeCipherValue = 0x01 + + ExtensionSNI = 0x00 ) var ( diff --git a/mtglib/internal/telegram/init.go b/mtglib/internal/telegram/init.go index dc8b45b..af707d3 100644 --- a/mtglib/internal/telegram/init.go +++ b/mtglib/internal/telegram/init.go @@ -36,7 +36,7 @@ var ( {network: "tcp4", address: "149.154.167.91:443"}, }, { - {network: "tcp4", address: "149.154.171.5:443"}, + {network: "tcp4", address: "149.154.171.5:443"}, }, } v6Addresses = [5]tgAddr{ diff --git a/mtglib/proxy.go b/mtglib/proxy.go index 4096438..b6a8f12 100644 --- a/mtglib/proxy.go +++ b/mtglib/proxy.go @@ -144,6 +144,13 @@ func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) bool { return false } + if hello.Host != "" && hello.Host != p.secret.Host { + p.logger.BindStr("hostname", hello.Host).Info("incorrect domain was found in SNI") + p.doDomainFronting(ctx, rewind) + + return false + } + if err := p.timeAttackDetector.Valid(hello.Time); err != nil { p.logger.InfoError("invalid faketls time", err) p.doDomainFronting(ctx, rewind)