Detecting GFW DNS poisoning: type-A answers to non-type-A queries

I got a tip about a trick to check for poisoned DNS responses by the GFW. The GFW injector always responds with a single A record, even when the query is for a different record type, like TXT or AAAA.

Because of the bidirectional nature of the GFW’s DNS injection, you can test this even from outside the firewall. Just send a DNS query for a blocked name to some IP address inside of China, and you will get an injected response.

Example non-poisoned domain (example.com). Note status: REFUSED and ANSWER: 0.

$ dig -t TXT @dns2.edu.cn example.com

; <<>> DiG 9.11.5-P4-5.1-Debian <<>> -t TXT @dns2.edu.cn example.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 22796
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;example.com.                   IN      TXT

;; Query time: 241 msec
;; SERVER: 2001:da8:1:100::13#53(2001:da8:1:100::13)
;; WHEN: Wed May 06 19:59:30 MDT 2020
;; MSG SIZE  rcvd: 40

Example poisoned domain (torproject.org). Note status: NOERROR, ANSWER: 1, and A 4.36.66.178 even though we asked for TXT.

$ dig -t TXT @dns2.edu.cn torproject.org

; <<>> DiG 9.11.5-P4-5.1-Debian <<>> -t TXT @dns2.edu.cn torproject.org
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37395
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;torproject.org.                        IN      TXT

;; ANSWER SECTION:
torproject.org.         253     IN      A       4.36.66.178

;; Query time: 231 msec
;; SERVER: 2001:da8:1:100::13#53(2001:da8:1:100::13)
;; WHEN: Wed May 06 20:02:21 MDT 2020
;; MSG SIZE  rcvd: 48