mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-09-01 11:34:01 +03:00
Merge pull request #446 from runixer/fix/grease-cipher-suite
Fix DPI detection: replace GREASE cipher suite in FakeTLS ServerHello
This commit is contained in:
@@ -130,18 +130,25 @@ func parseHandshake(r io.Reader) (*ClientHello, error) {
|
|||||||
|
|
||||||
cipherSuiteLen := int64(binary.BigEndian.Uint16(header[:]))
|
cipherSuiteLen := int64(binary.BigEndian.Uint16(header[:]))
|
||||||
|
|
||||||
// we do not care about picking up any cipher. we pick the first one,
|
// Pick the first non-GREASE cipher suite from the list.
|
||||||
// so it is always should be present.
|
// Real TLS servers never select GREASE values (RFC 8701, pattern 0x?a?a),
|
||||||
if _, err := io.ReadFull(r, header[:]); err != nil {
|
// so echoing them back is a trivial DPI fingerprint.
|
||||||
return nil, fmt.Errorf("cannot read first cipher suite: %w", err)
|
for remaining := cipherSuiteLen; remaining >= 2; remaining -= 2 {
|
||||||
|
if _, err := io.ReadFull(r, header[:]); err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot read cipher suite: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := binary.BigEndian.Uint16(header[:])
|
||||||
|
if hello.CipherSuite == 0 && cs&0x0f0f != 0x0a0a {
|
||||||
|
hello.CipherSuite = cs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hello.CipherSuite = binary.BigEndian.Uint16(header[:])
|
if hello.CipherSuite == 0 {
|
||||||
|
hello.CipherSuite = 0x1301 // fallback: TLS_AES_128_GCM_SHA256
|
||||||
if _, err := io.CopyN(io.Discard, r, cipherSuiteLen-2); err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot skip remaining cipher suites: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(r, header[:1]); err != nil {
|
if _, err := io.ReadFull(r, header[:1]); err != nil {
|
||||||
return nil, fmt.Errorf("cannot read compression methods length: %w", err)
|
return nil, fmt.Errorf("cannot read compression methods length: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,12 +234,13 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCipherSuiteLe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadFirstCipherSuite() {
|
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadFirstCipherSuite() {
|
||||||
body := make([]byte, 2+fake.RandomLen+1+2)
|
body := make([]byte, 2+fake.RandomLen+1+2+1) // cipherSuiteLen=2 but only 1 byte available
|
||||||
|
binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 2)
|
||||||
|
|
||||||
suite.writeBody(body)
|
suite.writeBody(body)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read first cipher suite")
|
suite.ErrorContains(err, "cannot read cipher suite")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipRemainingCipherSuites() {
|
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipRemainingCipherSuites() {
|
||||||
@@ -249,7 +250,7 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipRemainingCiph
|
|||||||
suite.writeBody(body)
|
suite.writeBody(body)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot skip remaining cipher suites")
|
suite.ErrorContains(err, "cannot read cipher suite")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCompressionMethodsLength() {
|
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCompressionMethodsLength() {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func (suite *SendServerHelloTestSuite) TestRecordStructure() {
|
|||||||
recordType, length, err := tls.ReadRecord(suite.buf, &rec)
|
recordType, length, err := tls.ReadRecord(suite.buf, &rec)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal(byte(tls.TypeApplicationData), recordType)
|
suite.Equal(byte(tls.TypeApplicationData), recordType)
|
||||||
suite.Greater(length, int64(2500))
|
suite.GreaterOrEqual(length, int64(2500))
|
||||||
|
|
||||||
suite.Empty(suite.buf.Bytes())
|
suite.Empty(suite.buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"time": 1617181365,
|
||||||
|
"random": "w4TaDfYg/aUKdx1oi68vxMKvHJczRNvtRRppLETzeNE=",
|
||||||
|
"sessionId": "St2BZ2uHMFn3B2trD1jfdtpjoJOOg6JBeLhFcyCMCq4=",
|
||||||
|
"host": "storage.googleapis.com",
|
||||||
|
"cipherSuite": 4867,
|
||||||
|
"full": "FgMBAgIBAAH+AwPDhNoN9iD9pQp3HWiLry/Ewq8clzNE2+1FGmksRPN40SBK3YFna4cwWfcHa2sPWN922mOgk46DokF4uEVzIIwKrgA2WloTAxMBEwLALMArwCTAI8AKwAnMqcAwwC/AKMAnwBTAE8yoAJ0AnAA9ADwANQAvwAjAEgAKAQABf/8BAAEAAAAAGwAZAAAWc3RvcmFnZS5nb29nbGVhcGlzLmNvbQAXAAAADQAYABYEAwgEBAEFAwIDCAUIBQUBCAYGAQIBAAUABQEAAAAAM3QAAAASAAAAEAAwAC4CaDIFaDItMTYFaDItMTUFaDItMTQIc3BkeS8zLjEGc3BkeS8zCGh0dHAvMS4xAAsAAgEAADMAJgAkAB0AIAf+6C8fSRJSAC7CyUvdR9kDclNR9KLCsCFHpVZ3bC8iAC0AAgEBACsACQgDBAMDAwIDAQAKAAoACAAdABcAGAAZABUAoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user