def add_attack_answer(response: dns.message.Message) -> None:
+ """
+ Crafted authoritative response to <q>.evil.f217.hack./A
+
+ ;; ANSWER
+ <q>.evil.f217.hack. 300 IN A 192.0.2.217
+ <q>.evil.f217.hack. 300 IN RRSIG A 13 1 300 <exp> <inc> 12345 evil.f217.hack. <base64 of 64×0x00>
+ ^^^ Labels = 1, qname has 4 labels, wildcard heuristic fires
+
+ ;; AUTHORITY (single owner, three rdatasets in this wire order)
+ 00000000.evil.f217.hack. 300 IN NSEC zzz.evil.f217.hack. A RRSIG NSEC
+ 00000000.evil.f217.hack. 300 IN RRSIG NSEC 13 4 300 <exp> <inc> 12345 evil.f217.hack. <base64 of 64×0x00>
+ 00000000.evil.f217.hack. 300 IN NSEC3 1 0 0 - VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV A RRSIG
+ """
+ # A + RRSIG
response.answer.append(rrset(ATTACK, dns.rdatatype.A, FORGED_A))
response.answer.append(garbage_rrsig(ATTACK, dns.rdatatype.A, 1, CHILD))
-
+ # NSEC
nsec = rrset(
NSEC_OWNER,
dns.rdatatype.NSEC,
f"{NSEC_NEXT} A RRSIG NSEC",
)
- nsec3 = rrset(
- NSEC_OWNER,
- dns.rdatatype.NSEC3,
- "1 0 0 - VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV A RRSIG",
- )
response.authority.append(nsec)
+ # RRSIG(NSEC)
response.authority.append(
garbage_rrsig(
NSEC_OWNER,
CHILD,
)
)
+ # NSEC3
+ nsec3 = rrset(
+ NSEC_OWNER,
+ dns.rdatatype.NSEC3,
+ "1 0 0 - VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV A RRSIG",
+ )
response.authority.append(nsec3)
server = AsyncDnsServer(default_aa=True)
server.install_response_handlers(RuntimeCheckHandler(load_key()))
server.run()
+
+
+if __name__ == "__main__":
+ main()
pytestmark = pytest.mark.extra_artifacts(
[
- "ans*/ans.run",
- "keys.json",
+ "ans1/ans.run",
+ "ans1/keys.json",
]
)
def bootstrap():
keys = {ZONE: _make_key()}
- Path("keys.json").write_text(json.dumps(keys, indent=2), encoding="ascii")
+ Path("ans1/keys.json").write_text(json.dumps(keys, indent=2), encoding="ascii")
zone_dnskey = "".join(keys[ZONE]["dnskey"].split()[3:])
return {"ZONE_DNSKEY": zone_dnskey}
assert rrsig[0].labels == labels, response.to_text()
-def test_repro_5985_direct_findnoqname_addnoqname_mismatch_fixture():
+def test_malicious_findnoqname_addnoqname_mismatch():
response = _query(AUTH, ATTACK, "A")
isctest.check.noerror(response)
assert _has_a(response, response.answer, ATTACK, FORGED_A), response.to_text()
_check_rrsig(response, response.answer, ATTACK, dns.rdatatype.A, CHILD, labels=1)
+ # Has NSEC
assert _rrset(response, response.authority, NSEC_OWNER, dns.rdatatype.NSEC)
_check_rrsig(response, response.authority, NSEC_OWNER, dns.rdatatype.NSEC, CHILD)
+ # Has NSEC3
assert _rrset(response, response.authority, NSEC_OWNER, dns.rdatatype.NSEC3)
assert (
_rrset(
)
-def test_repro_5985_resolver_does_not_abort_on_noqname_type_mismatch():
+def test_resolver_findnoqname_addnoqname_mismatch():
+ # Send one trigger query
_query(RESOLVER, ATTACK, "A")
-
response = _query(RESOLVER, ZONE, "SOA")
isctest.check.noerror(response)