New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bidirectional DNS, HTTPS, HTTP injection in Turkmenistan #80
Comments
@censoredplanet, @reethikar, @ramakrishnansr: I imagine you have have evidence for DNS injection of twitter.com in Turkmenistan in Satellite data. You may also know other names that get injected. Is it convenient for you to check? Or, how would someone go about looking for country-specific injections in the raw data? |
Yes, it is possible that we observe this phenomenon. @eltsai is currently working with @censoredplanet Satellite data. Elisa, can you please provide some information on whether we see this injection? |
@wkrp thank you for sharing this! From the Satellite scan, we saw evidence of DNS injection for
|
Going through the block list above, one domain This shares a high similarity with a previous report about users from China unable to resolve x3 while x1, x2, x4 are not affected. There are multiple posts like this and all report that x3 is blocked in China (earliest post goes back to Jan 2020). Some user claims that they ran catchpoint test in China and that x3's availability is only 10% of that of x1, but unfortunately details are lost. On a side note, in that report, users also speculate about reasons why only x3 is blocked. If the block is intentional, GFW probably would block the other three issuers as well. The following is the bogus DNS response users from China were seeing:
They suspect that the CNAME assigned by akamai might be "contaminated" due to being linked previously to a blocked website. They tested that This seems interesting to me because we are observing exactly the same policy of DNS interference in Turkmenistan, including x1,2,4 not being affected and that 'a771.dscq.akamai.net' triggers interference. Are the censors from the two countries sharing block list in some way .. (?) EDIT On a closer look, the policy is slightly different between China & TM's DNS injector wrt
*.akamai.net* seems always to trigger injection on the TM resolver tested. So they do have different policies. |
Quote @diwenx: Seems like for
|
Thanks for checking! It's wonderful that we now live in a world where we can answer questions like these quickly, because the necessary data already exist. This is thanks to you and all who have worked to build capable censorship measurement platforms. An interesting feature of the DNS injection list I want to call out: many of the names are DNS over HTTP or DNS over TLS servers.
Their presence on the list looks like an attempt to block encrypted DNS, which is not vulnerable to injected responses the way plaintext DNS is. For example, I tested the domains of a handful of the DoH resolvers listed at the curl wiki, and most of them also experience injection:
A further interesting point about DoH is that there is no injection for the canary domain
Even if you did know an IP address for a DoH resolver in advance, you likely still would not be able to use it. I didn't mention yet that RST injection in response to TLS SNI in Turkmenistan is also externally measurable:
The
The SNI blocklist is not the same as the DNS blocklist, though. For example, twitter.com is on both lists, but facebook.com is only on the SNI blocklist, and msn.com is only on the DNS blocklist.
A quick test also shows that there is no injection with UDP over IPv6:
|
That's a great find—thanks for finding this connection. I tried making a CNAME on my own domain, pointing to
I confirm that there seems to be a DNS injection rule for I may have an explanation for the blocking of
As I understand it, this encoding scheme existed for compatibility with pre-HTTP/1.1 clients that do not send the Host header. I don't know whether it still works that way. HTTPS URLs under
Possibly as a result of being used for circumvention purposes, a248.e.akamai.net become blocked by DNS injection in China in September 2014:
|
We confirm this from the Hyperquack HTTP and HTTPS data. From HTTPS data in Turkmenistan during 2020, we see 82 domains are experiencing TCP reset after Sep 13 2020 (a lot of domains owned by Google):
From the Hyperquack HTTP data, we see 45 domains are almost blocked all year long, except a temporal removal in a single netblock (
Interestingly, I did not see a sudden drop in traffic in the Google Transparency Report in Turkmenistan, not for Web Search at least. I can see a decrease in the traffic for Youtube and Google Sites, not sure if this is because of the blocking. |
Thanks for checking. I can reproduce your findings. Here are some more characteristics of the HTTP and SNI injection:
You can demonstrate the injection easily using curl or OpenSSL s_client. First, get an IP address in Turkmenistan:
The host happens to be an HTTP and HTTPS server. In the case of no injection, we expect to see an HTTP response or a TLS Server Hello with the server's normal certificate:
In contrast, when there is an injection, we instead get an error showing that a RST was received:
Injection occurs even if the client sends HTTP or HTTPS to the "wrong" port out of 80 and 443, but not on other ports:
In my tests, packets that are actually from 95.85.120.6 (such as the SYN/ACK) have TTL 47, while the RSTs have TTL 113. The matching rules are not the same for all three forms of injection. You can see that in the differing lengths of the lists @eltsai posted. For example:
But even for domains that are common to two or more lists, wildcard rules may differ. For example, the HTTP rule matches
|
@ValdikSS reports that any HTTP request with a Host header that contains the string "vpn" gets an immediate RST. The same rule does not exist for HTTPS SNI. Only lowercase "vpn" is matched.
|
@wkrp First of all, I would like to thank you all for your work on censorship analysis. I am a resident of Turkmenistan and after reading this I was glad that someone was analyzing all this. I understand that several blocking methods are used, but could you tell about the methods for bypassing them? For its part, I am ready to provide any data. Thanks |
@cenZnah I don't know what works best in Turkmenistan currently. NTC has a sub-forum dedicated to Turkemenistan; you can check some threads there: https://ntc.party/c/internet-censorship-all-around-the-world/turkmenistan/17 E.g.
There's a good chance that Tor Browser with a private obfs4 bridge will work. You can request a private bridge by emailing frontdesk@torproject.org: see instructions. According to the Psiphon dashboard, there are over 2K daily unique users in Turkmenistan in the last week, so it is worth trying Psiphon.
Thank you for the offer of help. It has been hard to make contact with users in Turkmenistan. Here are some threads with requests for assistance:
Let me know if you have difficulty accessing any of these links. We can also establish some efficient communications channels for faster feedback if necessary. |
I tried the Turkmenistan DNS injector again today, and it looks like it has changed in this regard. It no longer keeps data from the query after the first Question. That means it no longer produces malformed responses to EDNS queries. See how the $ (printf '\x12\x34\x01\x20\x00\x01\x00\x00\x00\x00\x00\x00\x07twitter\x03com\x00\x00\x01\x00\x01\xff\xff\xff\xff'; sleep 1) | ncat --udp $TARGET 53 -x /dev/stderr >/dev/null [0000] 12 34 01 20 00 01 00 00 00 00 00 00 07 74 77 69 .4. .... .....twi [0010] 74 74 65 72 03 63 6F 6D 00 00 01 00 01 FF FF FF tter.com ........ [0020] FF . [0000] 12 34 81 80 00 01 00 01 00 00 00 00 07 74 77 69 .4...... .....twi [0010] 74 74 65 72 03 63 6F 6D 00 00 01 00 01 FF FF FF tter.com ........ [0020] FF C0 0C 00 01 00 01 00 00 01 2C 00 04 7F 00 00 ........ ..,..... [0030] 01 . 2022-11-17 $ (printf '\x12\x34\x01\x20\x00\x01\x00\x00\x00\x00\x00\x00\x07twitter\x03com\x00\x00\x01\x00\x01\xff\xff\xff\xff'; sleep 1) | ncat --udp $TARGET 53 -x /dev/stderr >/dev/null [0000] 12 34 01 20 00 01 00 00 00 00 00 00 07 74 77 69 .4. .... .....twi [0010] 74 74 65 72 03 63 6F 6D 00 00 01 00 01 FF FF FF tter.com ........ [0020] FF . [0000] 12 34 81 80 00 01 00 01 00 00 00 00 07 74 77 69 .4...... .....twi [0010] 74 74 65 72 03 63 6F 6D 00 00 01 00 01 C0 0C 00 tter.com ........ [0020] 01 00 01 00 00 01 2C 00 04 7F 00 00 01 ......,. .....
A weird consequence is that a query that has 2 Questions, where only the 2nd Question is filtered, will have a response that refers to only the first, non-filtered name. $ (printf '\x12\x34\x01\x20\x00\x02\x00\x00\x00\x00\x00\x00\x03foo\x03com\x00\x00\x01\x00\x01\x07twitter\x03com\x00\x00\x01\x00\x01'; sleep 1) | ncat --udp $TARGET 53 -x /dev/stderr >/dev/null [0000] 12 34 01 20 00 02 00 00 00 00 00 00 03 66 6F 6F .4. .... .....foo [0010] 03 63 6F 6D 00 00 01 00 01 07 74 77 69 74 74 65 .com.... ..twitte [0020] 72 03 63 6F 6D 00 00 01 00 01 r.com... .. [0000] 12 34 81 80 00 01 00 01 00 00 00 00 03 66 6F 6F .4...... .....foo [0010] 03 63 6F 6D 00 00 01 00 01 07 74 77 69 74 74 65 .com.... ..twitte [0020] 72 03 63 6F 6D 00 00 01 00 01 C0 0C 00 01 00 01 r.com... ........ [0030] 00 00 01 2C 00 04 7F 00 00 01 ...,.... .. 2022-11-17 $ (printf '\x12\x34\x01\x20\x00\x02\x00\x00\x00\x00\x00\x00\x03foo\x03com\x00\x00\x01\x00\x01\x07twitter\x03com\x00\x00\x01\x00\x01'; sleep 1) | ncat --udp $TARGET 53 -x /dev/stderr >/dev/null [0000] 12 34 01 20 00 02 00 00 00 00 00 00 03 66 6F 6F .4. .... .....foo [0010] 03 63 6F 6D 00 00 01 00 01 07 74 77 69 74 74 65 .com.... ..twitte [0020] 72 03 63 6F 6D 00 00 01 00 01 r.com... .. [0000] 12 34 81 80 00 01 00 01 00 00 00 00 03 66 6F 6F .4...... .....foo [0010] 03 63 6F 6D 00 00 01 00 01 C0 0C 00 01 00 01 00 .com.... ........ [0020] 00 01 2C 00 04 7F 00 00 01 ..,..... .
Replacing the $ (printf '\x12\x34\x01\x20\x00\x01\x00\x00\x00\x00\x00\x00\x07twitter\x03com\xc0\x04\x00\x01\x00\x01'; sleep 1) | ncat --udp $TARGET 53 -x /dev/stderr >/dev/null [0000] 12 34 01 20 00 01 00 00 00 00 00 00 07 74 77 69 .4. .... .....twi [0010] 74 74 65 72 03 63 6F 6D C0 04 00 01 00 01 tter.com ...... [0000] 12 34 81 80 00 01 00 01 00 00 00 00 07 74 77 69 .4...... .....twi [0010] 74 74 65 72 03 63 6F 6D C0 04 00 01 00 01 C0 0C tter.com ........ [0020] 00 01 00 01 00 00 01 2C 00 04 7F 00 00 01 ......., ...... 2022-11-17 $ (printf '\x12\x34\x01\x20\x00\x01\x00\x00\x00\x00\x00\x00\x07twitter\x03com\xc0\x04\x00\x01\x00\x01'; sleep 1) | ncat --udp $TARGET 53 -x /dev/stderr >/dev/null [0000] 12 34 01 20 00 01 00 00 00 00 00 00 07 74 77 69 .4. .... .....twi [0010] 74 74 65 72 03 63 6F 6D C0 04 00 01 00 01 tter.com ...... |
Not sure that's valuable, but related to the topic - found DNS server in Turkmenistan which could be used to check for DNS injection: 217.174.239.141
|
Hi @starlancer1 , thanks for sharing! You probably already knew this, but we are sharing it here just in case. When measuring the DNS injections, it is sometimes even more desirable to send DNS queries to a non-DNS server/resolver for testing. This way, you can guarantee that the DNS responses you received are injected by some middle boxes, rather than some real DNS servers/resolvers. This measurement design gives you zero false positive in detection. For example, in your case, you need to somehow validate the responses from But if you send queries to a non-DNS server/resolver like |
@gfw-report actually I did not, so thanks for sharing - all clear now! |
While investigating a recent change in Tor metrics in Turkmenistan, I found a DNS injection that can be triggered by sending DNS queries into the country. Query for any name matching the pattern
*twitter.com*
will receive an injected response, specifically an A record pointing to the IP address 127.0.0.1.I don't doubt that someone local to the region has already discovered this, but I did not find anything in a quick web search, so I'm documenting what I found here.
I only tested the outside-in direction. I did not actually verify that the injection occurs from a vantage inside Turkmenistan, but I would be surprised if that were not the case.
Summary
*twitter.com*
.twitter.com
causes injection, as do names with prefixes and suffixes:x.twitter.com
,xtwitter.com
,twitter.com.x
,twitter.comx
.c00c000100010000012c00047f000001
.Demonstration using dig
We will need an IP address in Turkmenistan to send queries to:
Issue a query, using the Turkmenistan IP address as the resolver:
Look at
status: NOERROR
andQUERY: 1, ANSWER: 1
. This means the query received a response with one answer. TheANSWER SECTION
shows that according to the injected response, the name resolves to 127.0.0.1.Analysis of payloads
Let's take a look at what happens at the DNS protocol level. From a packet capture, we see that the 29 bytes of the query message are:
12 34
01 20
00 01
00 00
00 00
00 00
07 74 77 69 74 74 65 72
07
is the length of the label, and74 77 69 74 74 65 72
spellstwitter
.03 63 6f 6d
03
is the length and63 6f 6d
spellscom
.00
00 01
00 01
The 45 bytes of the injected response are almost the same. I've marked the changed or added bytes with
()
:The response is the same as the query, except for these changes:
0120
to8180
(now with QR=1, indicating a response message).0000
to0001
(indicating a single resource record in the Answer section),c00c000100010000012c00047f000001
.Let's analyze the suffix, which syntactically forms a resource record in the response's Answer section.
c0 0c
c00c
is a compression pointer that points to byte offset 12, the first name in the Question section; i.e., the name that appeared in the query.00 01
00 01
00 00 01 2c
00 04
7f 00 00 01
Where response construction goes wrong
The simplistic technique of appending a fixed resource record to an observed query only works when the query does not contain any resource records after the Question section. If it does, then the injector will simply reflect those resource records back to the requester, along with its additional suffix. The requester will interpret the first of its own resource records in the response as belonging to the Answer section, and the injector's added suffix will appear as unused trailing bytes.
This can be easily demonstrated by running dig without the
+noedns
option. By default, dig sends queries with EDNS, which means they have a non-empty Additional section. The injector appends what it intends to be the response's Answer section without first removing the query's Additional section. The requester sees its own Additional section as being the Answer section.Note the EDNS
OPT PSEUDOSECTION
andWARNING: Message has 16 extra bytes at end
.Despite the response message being syntactically broken, it likely still has the intended effect of preventing name resolution.
Further experiments with netcat
For better control over the content of queries, we can construct our own UDP packets and send them using netcat. The
-x
option of Ncat is convenient for getting a hex dump. For example, here, the first two lines are the outgoing query and the next three lines are the incoming (injected) response.Queries sent to ports other then 53 (for example port 12345) also get injected. Even port 0 works.
If you set QR=1 (change FLAGS from
0120
to8120
), you do not get an injection.But, you can set bit in FLAGS other than QR (i.e. FLAGS=
7fff
), and you will get an injection. The response's FLAGS are overwritten to8180
as usual.Sending a query with QTYPE=AAAA (
00 1c
) still results in an injected A-type response.You can also freely change QCLASS (here
ff ff
).But, it seems that QCLASS≠IN only gets an injection on port 53! On other ports, it does not.
And if you truncate the query before QTYPE and QCLASS, you do not get an injection.
Any trailing bytes in the query (here
ff ff ff ff
) are retained, leading to a syntactically invalid response, as in the EDNS demonstration above.If you stick garbage before the Question section, you do not get an injection, which suggests there is an actual DNS parser in play, not just a simple byte pattern matcher.
Various name mutations show that the matching rule is probably
*twitter.com*
.twitter.com
may have an arbitrary prefix and suffix, even without an intervening name separator.twitter.com
witter.com
twitter.co
xtwitter.com
twitter.comx
x.twitter.com
twitter.com.x
You also get an injection if you represent the name
twitter.com
(invalidly) as a single 11-byte label (including the dot character in the middle), rather than as two labels of 7 and 3 bytes,twitter
andcom
. This suggests that the matching algorithm extracts the name from the query as a text string, then matches on the resulting string.The Question section may contain more than one entry, and the
twitter.com
entry does not have to be the first one. Notice that QDCOUNT=0001
in the response, not0002
as it was in the query.The injector's DNS parser does not seem to fully understand compression pointers, though. Indeed, the appearance of a compression pointer seems to halt interpretation of a name. If you replace the
00
null label at the end of the QNAME with either a forward or a backward pointer to some other00
byte in the message, you get an injection. But if you replace thecom
label with a pointer to acom
label elsewhere in the packet, you do not get an injection—even though such queries work fine with normal resolvers like 1.1.1.1:The text was updated successfully, but these errors were encountered: