InjNs - NS Record Injection
This module was designed for cache poisoning experiments, specifically for injecting unsolicited NS records into responses.
The module can handle queries for the following record types:
- A (Address Record)
- AAAA (IPv6 Address Record)
- CNAME (Canonical Name)
- DNAME (Delegation Name)
- MX (Mail Exchange)
- NS (Name Server)
- PTR (Pointer)
When a query for any of these record types is received, the module responds with a standard answer based on the requested type. Additionally, it includes an unsolicited NS record in the response, aiming for the resolver to pick it up and store it in its cache.
If successful, this means we can inject DNS records into the resolver's cache by including unsolicited records in the responses from the server-side.
Category: Cache poisoning
Format
injns[N].[3rp].[rpq].[adq].[rev].[als].[oi].yourdomain.com
Where:
- The
N
optional parameter is an arbitrary number used as an identifier appended to the injected domain name. This helps label and distinguish the specific domain name being injected. - The
3rp
optional parameter uses a 3rd-party parent domain (such as a3rdparty.net) for the injected record, instead of the yourdomain.com (default). If successful, the impact would be much more serious, as it would allow the injection of information for an arbitrary 3rd-party domain. - The
rpq
(replace question) optional parameter replaces the question in the query section with the domain name matching the injected record, potentially convincing the resolver that it originally requested the unsolicited record. - The
adq
(add question) optional parameter adds an additional question in the query section asking for the injected record, which could similarly convince the resolver that it requested the unsolicited record. - The
rev
(reverse) optional parameter reverses the order of answers — placing the unsolicited record first, followed by the actual answer to the query. - The
als
(all sections) optional parameter places the unsolicited records in all three sections (ANSWER, AUTHORITY and ADDITIONAL), instead of just in the ANSWER section. This approach can potentially increase the likelihood of the injected information being picked up and stored. - The
oi
(only injected) optional parameter causes the response to contain only the unsolicited record, omitting the actual answer to the query entirely.
Note that all these parameters can be combined with one another to produce different injection variants.
Examples
In this example, we demonstrate the basic functionality. By default, the injected record appears as the second answer, following the first (legitimate) answer. The injected record unsolicitedly resolves a subdomain under the same parent domain. In the subsequent query, we verify whether the resolver picked up and cached the unsolicited record:
# dig injns.yourdomain.com @10.211.55.2 ; <<>> DiG 9.18.10-2-Debian <<>> injns.yourdomain.com @10.211.55.2 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1414 ;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;injns.yourdomain.com. IN A ;; ANSWER SECTION: injns.yourdomain.com. 60 IN A 1.2.3.4 injected.yourdomain.com. 60 IN NS always345654.yourdomain.com. ;; Query time: 0 msec ;; SERVER: 10.211.55.2#53(10.211.55.2) (UDP) ;; WHEN: Tue Jan 14 13:24:26 +04 2025 ;; MSG SIZE rcvd: 118 # dig injected.yourdomain.com @10.211.55.2 +norecurse ; <<>> DiG 9.18.10-2-Debian <<>> injected.yourdomain.com @10.211.55.2 +norecurse ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 21517 ;; flags: qr aa; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;injected.yourdomain.com. IN A ;; Query time: 0 msec ;; SERVER: 10.211.55.2#53(10.211.55.2) (UDP) ;; WHEN: Tue Jan 14 13:24:26 +04 2025 ;; MSG SIZE rcvd: 41
We can see that the injected record was not cached, as we received NXDOMAIN
(No such name) status. This means that the injection did not succeed.
In this example, we use the identifier 123
to label the injected domain name, resulting in the generation of a specific domain name with the same label:
# dig injns123.yourdomain.com @10.211.55.2
; <<>> DiG 9.18.10-2-Debian <<>> injns123.yourdomain.com @10.211.55.2
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1765
;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;injns123.yourdomain.com. IN A
;; ANSWER SECTION:
injns123.yourdomain.com. 60 IN A 1.2.3.4
injected123.yourdomain.com. 60 IN NS always247407.yourdomain.com.
;; Query time: 3 msec
;; SERVER: 10.211.55.2#53(10.211.55.2) (UDP)
;; WHEN: Tue Jan 14 13:24:27 +04 2025
;; MSG SIZE rcvd: 124
This helps in identifying specific test cases during the testing process.
As mentioned earlier, this module can respond to various record types, not just A records. In this example, we request an DNAME record. As a result, we receive a valid DNAME record followed by an unsolicited NS record:
# dig DNAME injns.yourdomain.com @10.211.55.2
; <<>> DiG 9.18.10-2-Debian <<>> DNAME injns.yourdomain.com @10.211.55.2
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40672
;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;injns.yourdomain.com. IN DNAME
;; ANSWER SECTION:
injns.yourdomain.com. 60 IN DNAME always633015.yourdomain.com.
injected.yourdomain.com. 60 IN NS always164002.yourdomain.com.
;; Query time: 3 msec
;; SERVER: 10.211.55.2#53(10.211.55.2) (UDP)
;; WHEN: Tue Jan 14 13:24:29 +04 2025
;; MSG SIZE rcvd: 143
The module can handle A, AAAA, CNAME, DNAME, MX, NS and PTR record queries.
In the following example, we use the 3rp (3rd-party) parameter to request that the unsolicited record contain a subdomain under a 3rd-party parent domain. If successful, this scenario would have a much greater impact. In the subsequent query, we verify again whether the resolver picked up and cached the unsolicited record:
# dig injns123.3rp.yourdomain.com @10.211.55.2 ; <<>> DiG 9.18.10-2-Debian <<>> injns123.3rp.yourdomain.com @10.211.55.2 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31847 ;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;injns123.3rp.yourdomain.com. IN A ;; ANSWER SECTION: injns123.3rp.yourdomain.com. 60 IN A 1.2.3.4 injected123.a3rdparty.net. 60 IN NS always172175.yourdomain.com. ;; Query time: 3 msec ;; SERVER: 10.211.55.2#53(10.211.55.2) (UDP) ;; WHEN: Tue Jan 14 13:24:30 +04 2025 ;; MSG SIZE rcvd: 127 # dig injected123.a3rdparty.net @10.211.55.2 +norecurse ; <<>> DiG 9.18.10-2-Debian <<>> injected123.a3rdparty.net @10.211.55.2 +norecurse ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 52299 ;; flags: qr aa; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;injected123.a3rdparty.net. IN A ;; Query time: 7 msec ;; SERVER: 10.211.55.2#53(10.211.55.2) (UDP) ;; WHEN: Tue Jan 14 13:24:30 +04 2025 ;; MSG SIZE rcvd: 43
We can see that the injected record was not cached, as we received NXDOMAIN
(No such name) status. This means that the injection did not succeed.
In this example, we use the rev parameter to reverse the order of answers — the unsolicited record appears first, followed by the legitimate one. This arrangement could potentially increase the likelihood of the injected information being picked up and cached:
# dig injns123.3rp.rev.yourdomain.com @10.211.55.2
; <<>> DiG 9.18.10-2-Debian <<>> injns123.3rp.rev.yourdomain.com @10.211.55.2
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40523
;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;injns123.3rp.rev.yourdomain.com. IN A
;; ANSWER SECTION:
injected123.a3rdparty.net. 60 IN NS always344617.yourdomain.com.
injns123.3rp.rev.yourdomain.com. 60 IN A 1.2.3.4
;; Query time: 3 msec
;; SERVER: 10.211.55.2#53(10.211.55.2) (UDP)
;; WHEN: Tue Jan 14 13:24:31 +04 2025
;; MSG SIZE rcvd: 131
In this example, we use the rpq parameter to replace the question in the query section with information from the unsolicited record, in an attempt to convince the resolver that it originally requested this record:
# dig injns12345.rpq.yourdomain.com @10.211.55.2 ;; ;; Question section mismatch: got injected12345.yourdomain.com/NS/IN ;; communications error to 10.211.55.2#53: timed out ;; ;; Question section mismatch: got injected12345.yourdomain.com/NS/IN ;; communications error to 10.211.55.2#53: timed out ;; ;; Question section mismatch: got injected12345.yourdomain.com/NS/IN ;; communications error to 10.211.55.2#53: timed out ; <<>> DiG 9.18.10-2-Debian <<>> injns12345.rpq.yourdomain.com @10.211.55.2 ;; global options: +cmd ;; no servers could be reached
We can see that the client (dig) did not accept the response because the question (in the response) did not match the question in the original query. Here is the actual DNS response that was sent back to dig in this case:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 63906 ;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;injected12345.yourdomain.com. IN NS ;; ANSWER SECTION: injns12345.rpq.yourdomain.com. 60 IN A 1.2.3.4 injected12345.yourdomain.com. 60 IN NS always966523.yourdomain.com.
We can see that the question (query) was replaced to match the unsolicited record. The question remains as to how different resolvers would handle such a scenario — some resolvers may potentially accept this response.
In the next example, we use the adq parameter to add the question in the response asking for the unsolicited record, as another attempt to convince the resolver that it originally requested this record. We can see again that the client (dig) does not accept this response because the question does not match the original query:
# dig injns12345.adq.yourdomain.com @10.211.55.2 ;; Warning: Message parser reports malformed message packet. ;; ;; Question section mismatch: got injected12345.yourdomain.com/NS/IN ;; communications error to 10.211.55.2#53: timed out ;; Warning: Message parser reports malformed message packet. ;; ;; Question section mismatch: got injected12345.yourdomain.com/NS/IN ;; communications error to 10.211.55.2#53: timed out ;; Warning: Message parser reports malformed message packet. ;; ;; Question section mismatch: got injected12345.yourdomain.com/NS/IN ;; communications error to 10.211.55.2#53: timed out ; <<>> DiG 9.18.10-2-Debian <<>> injns12345.adq.yourdomain.com @10.211.55.2 ;; global options: +cmd ;; no servers could be reached
The question remains as to how different resolvers would handle this scenario. Here is the actual DNS response that was generated and returned to dig in this case:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27089 ;; flags: qr aa; QUERY: 2, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;injns12345.adq.yourdomain.com. IN A ;injected12345.yourdomain.com. IN NS ;; ANSWER SECTION: injns12345.adq.yourdomain.com. 60 IN A 1.2.3.4 injected12345.yourdomain.com. 60 IN NS always278121.yourdomain.com.
We can see that there are two questions — the original query and an additional one matching the unsolicited record.
In this example, the oi (only injected) parameter causes the answers to include only the injected (unsolicited) record, without the actual answer to the original query:
# dig injns.oi.yourdomain.com @10.211.55.2
; <<>> DiG 9.18.10-2-Debian <<>> injns.oi.yourdomain.com @10.211.55.2
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8043
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;injns.oi.yourdomain.com. IN A
;; ANSWER SECTION:
injected.yourdomain.com. 60 IN NS always985759.yourdomain.com.
;; Query time: 7 msec
;; SERVER: 10.211.55.2#53(10.211.55.2) (UDP)
;; WHEN: Tue Jan 14 13:25:06 +04 2025
;; MSG SIZE rcvd: 105
Note that the question remains the same as in the original query.
Here, we also request to replace the question in the query section with the unsolicited domain name. We can see that the dig client does not accept the response again because the question does not match the original query:
# dig injns.oi.rpq.yourdomain.com @10.211.55.2 ;; ;; Question section mismatch: got injected.yourdomain.com/NS/IN ;; communications error to 10.211.55.2#53: timed out ;; ;; Question section mismatch: got injected.yourdomain.com/NS/IN ;; communications error to 10.211.55.2#53: timed out ;; ;; Question section mismatch: got injected.yourdomain.com/NS/IN ;; communications error to 10.211.55.2#53: timed out ; <<>> DiG 9.18.10-2-Debian <<>> injns.oi.rpq.yourdomain.com @10.211.55.2 ;; global options: +cmd ;; no servers could be reached
The question remains as to how different resolvers would handle such a scenario. Here is the actual DNS response that was sent back to dig in this case:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 38354 ;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;injected.yourdomain.com. IN NS ;; ANSWER SECTION: injected.yourdomain.com. 60 IN NS always171856.yourdomain.com.
Some resolvers may potentially accept this response.
Lastly, in this example, we use the als (all sections) parameter to include the records in all sections of the DNS response:
# dig injns.als.yourdomain.com @10.211.55.2 ; <<>> DiG 9.18.10-2-Debian <<>> injns.als.yourdomain.com @10.211.55.2 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47961 ;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 2, ADDITIONAL: 2 ;; QUESTION SECTION: ;injns.als.yourdomain.com. IN A ;; ANSWER SECTION: injns.als.yourdomain.com. 60 IN A 1.2.3.4 injected.yourdomain.com. 60 IN NS always290880.yourdomain.com. ;; AUTHORITY SECTION: injns.als.yourdomain.com. 60 IN A 1.2.3.4 injected.yourdomain.com. 60 IN NS always290880.yourdomain.com. ;; ADDITIONAL SECTION: injns.als.yourdomain.com. 60 IN A 1.2.3.4 injected.yourdomain.com. 60 IN NS always290880.yourdomain.com. ;; Query time: 7 msec ;; SERVER: 10.211.55.2#53(10.211.55.2) (UDP) ;; WHEN: Tue Jan 14 13:25:23 +04 2025 ;; MSG SIZE rcvd: 282
This could potentially increase the likelihood of the injected information being picked up and stored.
Keep in mind that all these parameters can be combined to create different injection variants.
From the same category
- InjA - A Record Injection
- InjAaaa - AAAA Record Injection
- InjCname - CNAME Record Injection
- InjDname - DNAME Record Injection
- InjMx - MX Record Injection
- InjPtr - PTR Record Injection