Неработоспособность шифрованных протоколов (ShadowSocks/VMESS) (25.04.2024 +)

Блокируют по размеру пакетов, возможно, не по абсолютным цифрам, а по соотношению размера отправляемого к принимаемому.

HTTPS-соединения устанавливаются 1 раз из 6-7 (в дампе все неудачные), а HTTP устанавливаются всегда (у них другие размеры пакетов).

shadowsocks-block-tele2-29.04.2024.pcapng (44.7 KB)

Очень похоже на Случайная блокировка игры SCP Secret Laboratory на ТСПУ по UDP протоколу - #28 by ValdikSS

Без обсуждений.
Обсуждение в Обсуждение: неработоспособность ShadowSocks (30.10.2023 +)

Нашел как минимум один шаблон блокировки (для TCP):

  1. Клиент отсылает три пакета, каждый из которых содержит 411+ случайных байт
  2. Сервер отсылает произвольное количество любых байт чаще, чем клиент отправляет свои пакеты

shadowsocks-test1-tele2-29.04.2024.pcapng (3.1 KB)

Where was your test network?

Remote Testing with the Outline SDK

You can use the Outline SDK Tools to test different strategies remotely.

Here I test a fetch from Bee Line:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE"  https://ipinfo.io/org
AS16345 PJSC "Vimpelcom"

$SERVER_BEELINE is a socks5://... url pointing to a SOCKS5 server running on Bee Line.

Here is a blocked website:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|override:host=cloudflare.net" -method HEAD -v https://meduza.io
2024/04/29 15:49:50 HTTP request failed: Head "https://meduza.io": EOF

(I’m using override to bypass DNS blocking)

TLS Record fragmentation bypasses blocking:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|override:host=cloudflare.net|tlsfrag:1" -method HEAD -v https://meduza.io
[DEBUG] 2024/04/29 15:50:35.534519 main.go:106: Expires: [Mon, 29 Apr 2024 19:51:05 GMT]
[DEBUG] 2024/04/29 15:50:35.535194 main.go:106: Alt-Svc: [h3=":443"; ma=86400]
[DEBUG] 2024/04/29 15:50:35.535205 main.go:106: Cache-Control: [public, max-age=30]
[DEBUG] 2024/04/29 15:50:35.535211 main.go:106: Last-Modified: [Mon, 29 Apr 2024 19:25:15 GMT]
[DEBUG] 2024/04/29 15:50:35.535215 main.go:106: Cf-Cache-Status: [HIT]
[DEBUG] 2024/04/29 15:50:35.535220 main.go:106: X-Content-Type-Options: [nosniff]
[DEBUG] 2024/04/29 15:50:35.535225 main.go:106: Date: [Mon, 29 Apr 2024 19:50:35 GMT]
[DEBUG] 2024/04/29 15:50:35.535229 main.go:106: X-Cg-Cache-Status: [HIT]
[DEBUG] 2024/04/29 15:50:35.535233 main.go:106: Age: [168]
[DEBUG] 2024/04/29 15:50:35.535237 main.go:106: Strict-Transport-Security: [max-age=31536000; includeSubDomains; preload]
[DEBUG] 2024/04/29 15:50:35.535243 main.go:106: Server: [cloudflare]
[DEBUG] 2024/04/29 15:50:35.535247 main.go:106: Cf-Ray: [87c1d2664d33614a-KJA]
[DEBUG] 2024/04/29 15:50:35.535250 main.go:106: Content-Type: [text/html; charset=utf-8]
[DEBUG] 2024/04/29 15:50:35.535311 main.go:106: Connection: [keep-alive]

Testing Shadowsocks

To fetch with Shadowsocks:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$OUTLINE_SERVER" https://ipinfo.io/org
AS14061 DigitalOcean, LLC

Where $OUTLINE_SERVER is a ss:// Shadowsocks key.

Now we can do a two-hop test by piping the transports. Here we connect to Bee Line, then fetch ipinfo.io via the Outline Server. We seem to get timeouts for HTTP:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER" http://ipinfo.io/org
2024/04/29 15:58:02 HTTP request failed: Get "http://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1

And HTTPS:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER" https://ipinfo.io/org
2024/04/29 16:00:07 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1

When I use a prefix that simulates a TLS Handshake Record the HTTP fetch succeeds:

$ go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER?prefix=%16%03%01" http://ipinfo.io/org
AS14061 DigitalOcean, LLC

$ go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER?prefix=%16%03%01" https://ipinfo.io/org
AS14061 DigitalOcean, LLC

Note that my Outline Server is running on port 443.

I was able to get 10 successes in a row:

for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER?prefix=%16%03%01" https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC

Test 2: AS14061 DigitalOcean, LLC

Test 3: AS14061 DigitalOcean, LLC

Test 4: AS14061 DigitalOcean, LLC

Test 5: AS14061 DigitalOcean, LLC

Test 6: AS14061 DigitalOcean, LLC

Test 7: AS14061 DigitalOcean, LLC

Test 8: AS14061 DigitalOcean, LLC

Test 9: AS14061 DigitalOcean, LLC

Test 10: AS14061 DigitalOcean, LLC

But it’s not always successful. I don’t know if that’s an issue with my Bee Line being slow or if it’s inconsistent blocking:

$ for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER?prefix=%16%03%01" https://ipinfo.io/org; done
Test 1: 2024/04/29 16:10:14 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 2: 2024/04/29 16:10:20 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 3: 2024/04/29 16:10:26 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 4: AS14061 DigitalOcean, LLC

Test 5: AS14061 DigitalOcean, LLC

Test 6: AS14061 DigitalOcean, LLC

Test 7: AS14061 DigitalOcean, LLC

Test 8: 2024/04/29 16:10:41 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 9: 2024/04/29 16:10:47 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 10: AS14061 DigitalOcean, LLC

I’ll see if I can test in more networks. but it would be great if others could test too.

Did some testing on Megafon:

$ go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "socks5://$SERVER_MEGAFON"  https://ipinfo.io/org
AS31224 PJSC MegaFon

It’s not completely clear to me if the timeouts are blocking, or just slowness. I’ll need to change the code to configure that.

HTTPS, no prefix, port 443

for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "socks5://$SERVER_MEGAFON|$OUTLINE_SERVER" https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC

Test 2: 2024/04/29 16:16:13 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 3: AS14061 DigitalOcean, LLC

Test 4: AS14061 DigitalOcean, LLC

Test 5: 2024/04/29 16:16:23 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 6: AS14061 DigitalOcean, LLC

Test 7: 2024/04/29 16:16:34 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 8: AS14061 DigitalOcean, LLC

Test 9: 2024/04/29 16:16:42 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 10: AS14061 DigitalOcean, LLC

HTTP, no prefix, port 443

for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "socks5://$SERVER_MEGAFON|$OUTLINE_SERVER" http://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC

Test 2: AS14061 DigitalOcean, LLC

Test 3: AS14061 DigitalOcean, LLC

Test 4: 2024/04/29 16:17:04 HTTP request failed: Get "http://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 5: AS14061 DigitalOcean, LLC

Test 6: AS14061 DigitalOcean, LLC

Test 7: AS14061 DigitalOcean, LLC

Test 8: AS14061 DigitalOcean, LLC

Test 9: AS14061 DigitalOcean, LLC

Test 10: AS14061 DigitalOcean, LLC

HTTPS, TLS prefix, port 443

for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "socks5://$SERVER_MEGAFON|$OUTLINE_SERVER?prefix=%16%03%01" https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC

Test 2: AS14061 DigitalOcean, LLC

Test 3: AS14061 DigitalOcean, LLC

Test 4: AS14061 DigitalOcean, LLC

Test 5: AS14061 DigitalOcean, LLC

Test 6: AS14061 DigitalOcean, LLC

Test 7: AS14061 DigitalOcean, LLC

Test 8: AS14061 DigitalOcean, LLC

Test 9: AS14061 DigitalOcean, LLC

Test 10: AS14061 DigitalOcean, LLC

HTTP, TLS Prefix, Port 443

$ for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "socks5://$SERVER_MEGAFON|$OUTLINE_SERVER?prefix=%16%03%01" http://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC

Test 2: AS14061 DigitalOcean, LLC

Test 3: AS14061 DigitalOcean, LLC

Test 4: 2024/04/29 16:20:20 HTTP request failed: Get "http://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 5: AS14061 DigitalOcean, LLC

Test 6: 2024/04/29 16:20:32 HTTP request failed: Get "http://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 7: AS14061 DigitalOcean, LLC

Test 8: AS14061 DigitalOcean, LLC

Test 9: AS14061 DigitalOcean, LLC

Test 10: AS14061 DigitalOcean, LLC

While the fetch tool doesn’t have a timeout option, you can use our http2transport proxy + curl. I’m now getting more consistent results. I’m not seeing consistent blocking in either Bee Line or Megafon, so I think the timeouts were issues with my remote proxy. Again, it would be great to see direct measurements.

Run the local proxy:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/http2transport@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER"
2024/04/29 16:42:08 Proxy listening on 127.0.0.1:1080

Then use curl.

Bee Line

HTTP

$ for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS http://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: AS14061 DigitalOcean, LLC
Test 6: AS14061 DigitalOcean, LLC
Test 7: curl: (52) Empty reply from server
Test 8: curl: (52) Empty reply from server
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

HTTPS

for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: AS14061 DigitalOcean, LLC
Test 6: AS14061 DigitalOcean, LLC
Test 7: AS14061 DigitalOcean, LLC
Test 8: AS14061 DigitalOcean, LLC
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

Megafon

$ go run github.com/Jigsaw-Code/outline-sdk/x/examples/http2transport@latest -transport "socks5://$SERVER_MEGAFON|$OUTLINE_SERVER"
2024/04/29 16:47:08 Proxy listening on 127.0.0.1:1080

HTTP

$ for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS http://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: AS14061 DigitalOcean, LLC
Test 6: AS14061 DigitalOcean, LLC
Test 7: AS14061 DigitalOcean, LLC
Test 8: AS14061 DigitalOcean, LLC
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

HTTPS

$ for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: AS14061 DigitalOcean, LLC
Test 6: AS14061 DigitalOcean, LLC
Test 7: AS14061 DigitalOcean, LLC
Test 8: AS14061 DigitalOcean, LLC
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

MTS

go run github.com/Jigsaw-Code/outline-sdk/x/examples/http2transport@latest -transport "socks5://$SERVER_MTS|$OUTLINE_SERVER"
2024/04/29 16:59:14 Proxy listening on 127.0.0.1:1080

HTTP

$ for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS http://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: AS14061 DigitalOcean, LLC
Test 6: AS14061 DigitalOcean, LLC
Test 7: AS14061 DigitalOcean, LLC
Test 8: AS14061 DigitalOcean, LLC
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

HTTPS

$ for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: curl: (56) Failure when receiving data from the peer
Test 6: AS14061 DigitalOcean, LLC
Test 7: AS14061 DigitalOcean, LLC
Test 8: curl: (56) Recv failure: Connection reset by peer
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

Note: Shadowsocks doesn’t communicate TCP resets, so those resets are from the proxy in Russia, not the target website.

Для многочисленных тестов с подключением к одному dest_addr:dest_port возможен выбор src_addr:src_port из уже блокируемой 4-tuple (src_addr, src_port, dest_addr, dest_port). Новое подключение не отменяет блокировку.

It’s Tele2 cellular, Saint Petersburg.
I’ve also tested Beeline, MTS, Megafon and Yota @ Saint Petersburg, all are blocked.

You need to absolutely make sure that your ClientHello is large enough, as curl with mbedtls for example does not add padding extension and it does not hit 411 bytes length needed to trigger the block.

shadowsocks-notblock-yota-28.04.2024.pcapng (105,4 КБ)

Прикрепляю дамп с удачным подключением к серверу Hetzner из Уфы (Yota), подключение к Outline 162.55.42.178:24475 без префикса.

Блокировки прекратились с 30-го апреля на территории Башкортостана и Татарстана по всем провайдерам. Есть клиенты, которые жалуются на подключение с Билайна из Пензы, при этом чистый SS-блочится, а Outline с префиксом ClientHello не блокируется.

This is Outline traffic from opt-in metrics. You can see a gradual decline, supporting a partial block, and then it starts to go back up, aligning with @murka’s statement that blocking stopped on April 30.

Oh, I missed that the 411 bytes was required. With the Outline SDK, we can add a split to the Shadowsocks stream. The Outline Client doesn’t support that yet, but we want to move towards that. Perhaps the client can automatically try different splits if the connection fails.

На 2024-05-05T17:54:00Z блокировки полностью шифрованных протоколов сохраняются на мобильных Теле2, Мегафон, МТС, Билайн и Йота Санкт-Петербург.

Проверял на Shadowsocks, VMESS и собственном генераторе пакетов. Фильтруются все зарубежные направления (Россию не проверял).

Для эксперимента, развернул на московском vps shadowsocks. С мобильного Билайна не работает.

TCP. А UDP проверяли какие-нибудь для интереса?

How did you test Shadowsocks?

I tested Outline Shadowsocks on Tele2, Megafon, MTS and Beeline in St Petersburg, and was not able to observe blocking.

I tested using soax.com, an Outline Server, and the Outline SDK fetch tool. I tried both fetching a HTTPS page and a HTTP page. The HTTPS fetch doesn’t trigger the 411 bytes in the first packet, but the HTTP fetch was done with padding headers to force over 800 bytes in the first packet, and I still couldn’t trigger the block.

5 posts were merged into an existing topic: Обсуждение: неработоспособность ShadowSocks (30.10.2023 +)

A post was merged into an existing topic: Обсуждение: неработоспособность ShadowSocks (30.10.2023 +)

I’m using proxies on a probe (you have access to it) with v2fly configured with shadowsocks over probe’s proxy as an upstream, and sending the requests with regular curl (built with openssl) to HTTPS websites.

HTTP websites indeed do not trigger the block because the filter expects at least two client-server packets with sufficient amount of data (first is ClientHello, second is HTTP request), and HTTP has only one.

Check the PCAP in the top post.

Проверил QUIC (HTTPS-запросами) через Shadowsocks, на этих же каналах (Теле2, Билайн, МТС, Мегафон Санкт-Петербург) блокировки не вижу.

А фильтр проверяет только первые несколько пакетов (как при wireguard блокировках было) или все время за потоком данных следит?