Links
- Signal Post: https://signal.org/blog/help-iran-reconnect/
- Their Repository: https://github.com/signalapp/Signal-TLS-Proxy
- Our Original Issue: https://github.com/signalapp/Signal-TLS-Proxy/issues/3
Why Here?
⚠️ emotional. don't read.
So I've studied this with @studentmain and found it problematic about 4 hours ago.
We immediately reported this through a GitHub Issue, with PoC and advice attached, without even sleeping (it's about 4:00 am in local time)
But @moxie-signal (@moxie0) from @signalapp just closed our issue, saying this:
Hey everyone! Thanks for the interest in this. We normally don't use GH issues for this type of discussion, though, and prefer to have that happen over on the Signal community forum. Please check out https://community.signalusers.org/ if you want to get involved. Thanks!
It seemed that they just don't care about probing resistant and simply shut people's mouths up instead of fix this issue.
And guess what's next? THEY THEN JUST CLOSED THE WHOLE ISSUE FUNCTION!
THEY CLAIMED TO HELP PEOPLE IN CENSORSHIP, BUT THEY IN TURN CENSOR WHISTLEBLOWERS.
And here's what has happened if I go to "Signal community forum" to post the issue:

Okay, fine. You guys have really an awesome security!
Some updates: They even banned me from their GitHub organisation! What an honour!


Extra notes for those guys who think I was aggressive and not considerate enough:
You guys are correct. I ain't saint. I got my emotions and temper.
The weapon of criticism obviously cannot replace the criticism of weapons. Material force must be overthrown by material force. But theory also becomes a material force once it has gripped the masses.
So long as they were ignoring the weapon of criticism, I think it fair to put some criticism of weapons -- Or I can completely ignore everything: Don't post it at all, and grab some popcorn to watch -- Just wild guessing, Iranian being caught using Signal and sent to jail?
Updates:
So that's why I'll post it here.
Original Issue
Issue Body
I am not a specialist on Censorship Circumvention, so if I'm wrong, I'd appreciate if I could be corrected.
I've quickly scanned the code, so this is just a TLS tunnel set up to forward inner TLS traffic with signal SNIs as-is.
But what if the censor actively probe the suspected proxies? For example, connect and then use real signal app traffic and non-signal app traffic? The difference can be detected. And if a proxy can be detected, it can be blocked.
Thanks my friend @studentmain for the PoC.
PoC from @studentmain
PoC Source Code:
package main import ( "crypto/tls" "fmt" "log" "net" "os" "time" ) func send(addr, server, sni string) int { c0, e := net.Dial("tcp", addr) if e != nil { log.Fatal(e) } c1 := tls.Client(c0, &tls.Config{ ServerName: server, InsecureSkipVerify: true, }) c2 := tls.Client(c1, &tls.Config{ ServerName: sni, InsecureSkipVerify: true, }) c2.SetDeadline(time.Now().Add(2 * time.Minute)) s := fmt.Sprintf("GET / HTTP/1.1\r\nHost: %s\r\nUser-Agent: curl/7.68.0\r\n\r\n", sni) //b := make([]byte, 4096) l, _ := c2.Write([]byte(s)) log.Println(l) if e != nil { return 0 } log.Printf("%s->%s->%s\n", addr, server, sni) return l } func main() { if len(os.Args) != 3 { log.Fatalln("usage: main.exe server_name addr_port") } server := os.Args[1] addr := os.Args[2] l1 := send(addr, server, "updates.signal.org") l2 := send(addr, server, "telegram.org") if l1 != 0 && l2 == 0 { log.Fatalln("It is a Signal TLS Proxy") } log.Println("It is not a Signal TLS Proxy") }
PoC Example Run from @studentmain
Here is an example run of the PoC from @studentmain:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
C:\src\signalTlsPoc>main.exe
2021/02/05 03:40:15 usage: main.exe server_name addr_port
C:\src\signalTlsPoc>main.exe your.proxy.server.example.com your.server.ip.address:443
2021/02/05 03:40:34 69
2021/02/05 03:40:34 your.server.ip.address:443->your.proxy.server.example.com->updates.signal.org
2021/02/05 03:40:35 0
2021/02/05 03:40:35 your.server.ip.address:443->your.proxy.server.example.com->telegram.org
2021/02/05 03:40:35 It is a Signal TLS Proxy
C:\src\signalTlsPoc>main.exe baidu.com 39.156.69.79:443
2021/02/05 03:43:09 0
2021/02/05 03:43:09 39.156.69.79:443->baidu.com->updates.signal.org
2021/02/05 03:43:10 0
2021/02/05 03:43:10 39.156.69.79:443->baidu.com->telegram.org
2021/02/05 03:43:10 It is not a Signal TLS Proxy
C:\src\signalTlsPoc>
(modified to protect my personal information)
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEdbyjO018JNZhjd2SqnhRnCCMh0IFAmAcT0AACgkQqnhRnCCM
h0Lx6RAAlx6C6R7tZROlj8I+1uu2Owde9EjOA3BTYw7aC8pr4cd2LuTkmAY07TuZ
mbQRueUIKOXx2ZlTPRL1yOAW60nePC5SRmlOkNssafWzM+8zyqfHdRe64OX0ZA5V
KPgSVHOz2JKfh650oEqlaHb/6N/Nj++617TcHmmYoy/xsiDy/fD7IoifD8bT5N3+
4znZ2Sr2+IirKxqvpVDl5AwRBR5EfXUI54c0OjKB8Sq+VPlhAmAcj/eZgSz7A4rV
EWSJjwbN1fp7g2C4jNtjh0Z3jyvvA9yGNThylcMPbXcrc1a9sOzhbCgGTtF1413n
tXSjy3vRBEnwvlVs78ItQljCZV1S+jwpXlkjFPGAIYyRRQbvaQ4Biaki/4g3ofVu
nfqEnc5m4vSsde2r5GLBqxSkbsKuZjGBbd1KRE+5LZPvkVrA5wP400tirThnvNU9
G7S5Noz/8Npwazag0beDlio45e8sBafTBRJy/xuq6HVGdFkPa1C7EUMuDxVVnPdg
mOgyqrjXju+4y/pl39U6JmDt6/ZSn34/1EFVchRWS5Kq0gPDGdjvrjI88aOtqhRH
tT8F6dU8rwRa8tjMHv3gNVTQY+jQZIi4SHNB54DRc1K0AKzIkAOgD/ROhcU4LkXq
vudKRTfhbtL6l8FIo8WCzGVONK1dY+1alKXKc8cVJNrye/hm5t4=
=H1r/
-----END PGP SIGNATURE-----
Suggestion 1 from @DuckSoft
One crude idea is to use preshared tokens and fallback mechanism.
In this case, we can hide a preshared key within SNI. For example, we can preshare a key 538fd09a066a, and then mix this with original SNIs, where cdn.signal.org could become 538fd09a066a.cdn.signal.org. We then only accept whitelisted SNIs mixed with our preshared key, and deny all other SNIs including the original one cdn.signal.org. This way without knowing the preshared key, the censor is unable to active probe any longer. Since the SNI is actually sent inside the TLS tunnel after establishing a TLS connection with the proxy (TLS over TLS), the inside SNI is virtually invisible to the censors (unless root certificates got hijacked, etc.).(Edited: this is only feasible when those domains are transparent about SNIs and hosts, thus not operatable) And, since the tunnel is over TLS, you can virtually hide the preshared key anywhere inside, even making this as a customized TLS ClientHello Extension, or just masquerade as a plain HTTP request but with special headers, etc.
It is also possible to utilize WebSocket over TLS on the outside, so that unless the exact WebSocket endpoint path is known (which also works as a preshared key), the proxy cannot be probed by the censors without knowing the path - maybe they can only see a sad 404 page or your blog index page. But one thing must be clear: From the ClientHello ALPN extension, censors can deduce that you are using WebSocket techniques, since your ALPN cannot be h2/http/1.1 in this case, and WebSocket don't work when there's a h2 connection. You can turn off the h2 support on the server side and proceed in this situation, but normal browsers don't tend to connect WebSocket just all the time and they will prefer the use of h2 unless clearly denied.
For the fallback mechanism, when the traffic is found invalid, we should act the same as those innocent servers. When innocent servers is sent with TLS handshake inside TLS tunnel, the server tends to disconnect with bad request response. Then we should act the same. And if a valid HTTP request is received, we can then fallback to an innocent website, such as blog, minigames or something. This will significantly increase the difficulty for the censors to block.
Suggestion 2 from @DuckSoft
Also there's a weak temporal feature in this project, where the certificates tend to be newly signed. If possible, relay hosts should try to reuse existing certificates rather than brand new ones, which can be far more suspicious to the censors.
Suggestion 3 from @DuckSoft
Besides, for large portions of TLS traffic, there would be an ALPN in the Client Hello frame. If this proxy runs TLS over TLS, then the ALPN would possibly not be commonly seen ones like h2 and http/1.1. Instead, the application tend not to send this extension, which creates a passive feature of this proxy.
But what if we pretend to be h2/http/1.1? Well, you can actually do it, though you are breaking the rules. I am not sure about the exact behavior, but this could create problems when you want to be compatible with existing web servers.
Suggestion 4 from @DuckSoft
By the way, DPI (Deep Packet Inspection) techniques can detect TLS in TLS traffics easily (there are lots of papers available, if you do a search). Although not 100% TLS in TLS traffic is our lovely proxy, you can still risk being caught using a proxy when censorship is there.
DPI might be expensive, but code like @studentmain is not. The current status of probing resistance is really a problem.


