Skip to content

Fix DPI detection: replace GREASE cipher suite in FakeTLS ServerHello#446

Merged
9seconds merged 1 commit into
9seconds:masterfrom
runixer:fix/grease-cipher-suite
Apr 7, 2026
Merged

Fix DPI detection: replace GREASE cipher suite in FakeTLS ServerHello#446
9seconds merged 1 commit into
9seconds:masterfrom
runixer:fix/grease-cipher-suite

Conversation

@runixer
Copy link
Copy Markdown
Contributor

@runixer runixer commented Apr 6, 2026

Проблема

mtg эхом возвращает первый cipher suite из ClientHello клиента в ServerHello (client_side.go:139server_side.go:114). Telegram-клиенты (как и настоящие браузеры) ставят GREASE-значение (RFC 8701, паттерн 0x?a?a) первым в списке cipher suites.

Но настоящий TLS-сервер никогда не выбирает GREASE — он пропускает его и берёт первый реальный cipher suite (обычно 0x1301 TLS_AES_128_GCM_SHA256 для TLS 1.3). Эхо GREASE в ServerHello — это fingerprint, которого не бывает у легитимных TLS-реализаций.

DPI-системы могут проверять это поле и дропать соединения с GREASE cipher suite в ServerHello.

Диагностика

tcpdump на продакшн-сервере показал, что каждый ServerHello содержит GREASE-значение (0x3a3a, 0x5a5a, 0x7a7a, 0xaaaa и т.д.). При этом:

  1. TCP handshake (SYN/SYN-ACK/ACK) проходит нормально
  2. Клиент отправляет ClientHello — сервер ACK'ает
  3. Сервер отправляет ServerHello — клиент никогда не ACK'ает
  4. Сервер бесконечно ретрансмитит, клиент открывает новое соединение

После применения фикса ServerHello содержит 0x1301, и соединения, которые ранее дропались, начали работать.

Изменение

Перед записью cipher suite в ServerHello проверяем, соответствует ли значение GREASE-паттерну (value & 0x0f0f == 0x0a0a). Если да — заменяем на 0x1301 (TLS_AES_128_GCM_SHA256).

Поле CipherSuite не используется нигде в логике протокола — оно чисто косметическое для FakeTLS handshake.

@roman901
Copy link
Copy Markdown
Contributor

roman901 commented Apr 6, 2026

Учитывая, что TLS_AES_128_GCM_SHA256 в целом рекомендованный и самый популярный шифр - можно просто возвращать его и не проверять то, что нам пришло от пользователя
Подразумеваю, что в этот блок кода мы попадаем только когда мы поняли что гарантированно общаемся с TG-клиентом, а его сигнатура нам известна и мы точно знаем что он всегда слал 0x1301

@9seconds
Copy link
Copy Markdown
Owner

9seconds commented Apr 6, 2026

Про GREASE согласен, но почему мы выбираем этот шифр, а не что-то из списка дальше? Мне кажется правильный фикс - брать первый шифр из реальных

@runixer runixer force-pushed the fix/grease-cipher-suite branch from ecee6e7 to ca0f493 Compare April 6, 2026 20:07
@runixer
Copy link
Copy Markdown
Contributor Author

runixer commented Apr 6, 2026

Согласен, обновил. Посмотрел подробнее — Telegram-клиенты шлют два варианта списков:

  • С GREASE первым, за ним 0x1301 (TLS_AES_128_GCM_SHA256) — ~87%
  • Без GREASE, 0xc02b (TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) первым — ~13%

Теперь фикс в client_side.go: при парсинге итерируем список cipher suites и берём первый не-GREASE. server_side.go не трогаем. Фоллбэк на 0x1301 если все cipher suites — GREASE (теоретический случай).

Добавил snapshot-тест с GREASE в начале списка.

Instead of echoing the first cipher suite from ClientHello (which is
often a GREASE value like 0x5a5a), iterate the list and pick the first
real cipher suite. This is what real TLS servers do per RFC 8701.

Production data shows two client profiles:
- 87% send GREASE first, then 0x1301 (TLS_AES_128_GCM_SHA256)
- 13% send 0xc02b first (TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)

The fix correctly selects 0x1301 or 0xc02b respectively, matching
real server behavior. Fallback to 0x1301 if all suites are GREASE.

Add snapshot test with GREASE as first cipher suite.
@runixer runixer force-pushed the fix/grease-cipher-suite branch from ca0f493 to bec321d Compare April 6, 2026 20:16
@9seconds
Copy link
Copy Markdown
Owner

9seconds commented Apr 7, 2026

Красота!

@9seconds 9seconds merged commit 74a81a9 into 9seconds:master Apr 7, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants