A denial of service vulnerability exists in the DNSSEC DNSKEY Extended Flags functionality of BIND (version(s): 9.21.21). A specially crafted mirror domain can lead to a denial of service. An attacker can serve a malicious zone to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
BIND (version(s): 9.21.21)
BIND - https://www.isc.org/bind
7.5 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE-476 - NULL Pointer Dereference
The BIND nameserver is considered the reference implementation of the Domain Name System of the internet. It is capable of being an authoritative name server as well as a recursive cache for domain name queries on a network.
BIND9’s dst_key_fromdns() function in lib/dns/dst_api.c parses DNSKEY records received over the wire during DNSSEC validation. The function reads a 4-byte header (2 bytes flags, 1 byte protocol, 1 byte algorithm) and then checks for the DNS_KEYFLAG_EXTENDED flag (bit 12). When this flag is set, the function consumes an additional 2 bytes from the buffer as extended flags.
If an attacker crafts a DNSKEY RDATA where these 2 extended flag bytes are the only bytes following the 4-byte header, the buffer is left with 0 bytes remaining after parsing. The function then calls frombuffer(), which gates its algorithm_status() validation behind an isc_buffer_remaininglength(source) > 0 check. Because the buffer is empty, this check evaluates to false, and the algorithm validation is entirely skipped.
This allows a DNSKEY with an unsupported algorithm number (e.g., 39, which is unassigned in the IANA registry) to pass through unchecked. The get_key_struct() function sets key->func = dst_t_func[alg], which is NULL for any unregistered algorithm. The key object is returned successfully with a NULL function table pointer.
When the resolver later attempts to use this key for DNSSEC validation – for example, calling key->func->createctx in dst_context_create() – a NULL pointer dereference occurs, crashing the named process and causing denial of service for all clients relying on the resolver.
The DNS_KEYFLAG_EXTENDED flag originates from RFC 2535 (1999) and was intended to provide additional flag bits for the KEY record type. It was never adopted in practice and was effectively superseded by RFC 4034’s DNSKEY record. However, BIND9 still parses it for both KEY and DNSKEY records, creating this attack surface.
The extended flags are consumed by dst_key_fromdns():
lib/dns/dst_api.c:670-706
isc_result_t
dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass,
isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) {
...
if ((flags & DNS_KEYFLAG_EXTENDED) != 0) {
if (isc_buffer_remaininglength(source) < 2) {
return DST_R_INVALIDPUBLICKEY;
}
extflags = isc_buffer_getuint16(source);
flags |= (extflags << 16);
}
RETERR(frombuffer(name, alg, flags, proto, rdclass, source, mctx,
&key));
...
return ISC_R_SUCCESS;
}
The algorithm validation is conditionally skipped in the implementation of frombuffer() after calling get_key_struct().
lib/dns/dst_api.c:2125-2175
static isc_result_t
frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
unsigned int protocol, dns_rdataclass_t rdclass,
isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) {
...
key = get_key_struct(name, alg, flags, protocol, 0, rdclass, 0, mctx);
if (isc_buffer_remaininglength(source) > 0) {
result = algorithm_status(alg);
if (result != ISC_R_SUCCESS) {
dst_key_free(&key);
return result;
}
...
}
*keyp = key;
return ISC_R_SUCCESS;
}
The NULL function table is assigned in the implementation of get_key_struct():
.func = dst_t_func[alg],
lib/dns/dst_api.c:1419-1447
static dst_key_t *
get_key_struct(const dns_name_t *name, unsigned int alg, unsigned int flags,
unsigned int protocol, unsigned int bits,
dns_rdataclass_t rdclass, dns_ttl_t ttl, isc_mem_t *mctx) {
dst_key_t *key;
key = isc_mem_get(mctx, sizeof(dst_key_t));
*key = (dst_key_t){
...
.func = dst_t_func[alg],
};
...
}
The NULL dereference occurs when the key is used, e.g. in dst_context_create() (dst_api.c:274):
lib/dns/dst_api.c:264-300
isc_result_t
dst_context_create(dst_key_t *key, isc_mem_t *mctx, isc_logcategory_t category,
bool useforsigning, dst_context_t **dctxp) {
...
if (key->func->createctx == NULL) {
return DST_R_UNSUPPORTEDALG;
}
...
result = key->func->createctx(key, dctx);
...
}
When using the provided configuration against the server implemented by the proof-of-concept, the following NULL dereference will occur. This is because the instruction at address 0x55555561bfdb is dereferencing the key->func pointer.
$ gdb --args /path/to/named -c poc.conf -g
(gdb) g
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
2026-04-15T15:41:58.763-05:00 starting BIND 9.21.21 (Development Release) <id:ec2e1ce>
...
2026-04-15T15:41:58.763-05:00 compiled with OpenSSL version: OpenSSL 3.5.4 30 Sep 2025
2026-04-15T15:41:58.763-05:00 linked to OpenSSL version: OpenSSL 3.5.4 30 Sep 2025
2026-04-15T15:41:58.763-05:00 compiled with libuv version: 1.51.0
2026-04-15T15:41:58.763-05:00 linked to libuv version: 1.51.0
2026-04-15T15:41:58.763-05:00 compiled with liburcu-membarrier version: 0.15.6
2026-04-15T15:41:58.763-05:00 compiled with libnghttp2 version: 1.66.0
2026-04-15T15:41:58.763-05:00 linked to libnghttp2 version: 1.66.0
2026-04-15T15:41:58.763-05:00 compiled with libxml2 version: 2.12.10
2026-04-15T15:41:58.763-05:00 linked to libxml2 version: 21210
2026-04-15T15:41:58.763-05:00 compiled with json-c version: 0.18
2026-04-15T15:41:58.763-05:00 linked to json-c version: 0.18
2026-04-15T15:41:58.763-05:00 compiled with zlib version: 1.3.1.zlib-ng
2026-04-15T15:41:58.763-05:00 linked to zlib version: 1.3.1.zlib-ng
2026-04-15T15:41:58.763-05:00 ----------------------------------------------------
2026-04-15T15:41:58.763-05:00 BIND 9 is maintained by Internet Systems Consortium,
2026-04-15T15:41:58.763-05:00 Inc. (ISC), a non-profit 501(c)(3) public-benefit
2026-04-15T15:41:58.763-05:00 corporation. Support and training for BIND 9 are
2026-04-15T15:41:58.763-05:00 available at https://www.isc.org/support
2026-04-15T15:41:58.763-05:00 ----------------------------------------------------
...
[New Thread 0x7ffff6748680 (LWP 938075)]
2026-04-15T15:41:58.766-05:00 DNSSEC algorithms: RSASHA256 RSASHA512 ECDSAP256SHA256 ECDSAP384SHA384 ED25519 ED448 RSASHA256OID RSASHA512OID
2026-04-15T15:41:58.766-05:00 DS algorithms: SHA-1 SHA-256 SHA-384
2026-04-15T15:41:58.766-05:00 HMAC algorithms: HMAC-MD5 HMAC-SHA1 HMAC-SHA224 HMAC-SHA256 HMAC-SHA384 HMAC-SHA512
2026-04-15T15:41:58.766-05:00 TKEY mode 2 support (Diffie-Hellman): no
2026-04-15T15:41:58.766-05:00 TKEY mode 3 support (GSS-API): yes
...
2026-04-15T15:41:58.809-05:00 not using config file logging statement for logging due to -g option
...
2026-04-15T15:41:58.812-05:00 managed-keys-zone: loaded serial 9
2026-04-15T15:41:58.819-05:00 all zones loaded
2026-04-15T15:41:58.820-05:00 FIPS mode is disabled
2026-04-15T15:41:58.820-05:00 running
...
2026-04-15T15:41:59.253-05:00 zone example.com/IN: Transfer started.
2026-04-15T15:41:59.256-05:00 0x7fffe00fd580: transfer of 'example.com/IN' from 192.168.20.42#53: connected using 192.168.20.42#53
[New Thread 0x7fffaffff680 (LWP 938093)]
[New Thread 0x7fffaf7fe680 (LWP 938094)]
[New Thread 0x7fffaeffd680 (LWP 938095)]
[New Thread 0x7fffae7fc680 (LWP 938096)]
[New Thread 0x7fffadffb680 (LWP 938097)]
[New Thread 0x7fffad7fa680 (LWP 938098)]
[New Thread 0x7fffacff9680 (LWP 938099)]
[New Thread 0x7fffac7f8680 (LWP 938100)]
Thread 6 "isc-loop-0002" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffecdff680 (LWP 938078)]
0x000055555561bfdb in dst_context_create (key=0x7fffe0001010, mctx=0x555555be7e20, category=DNS_LOGCATEGORY_DNSSEC, useforsigning=0x0, dctxp=0x7fffecdf7510) at lib/dns/dst_api.c:274
Missing rpms, try: dnf --enablerepo='*debug*' install krb5-libs-debuginfo-1.22.2-3.fc43.x86_64 libnghttp2-debuginfo-1.66.0-2.fc43.x86_64 glibc-debuginfo-2.42-10.fc43.x86_64 openssl-libs-debuginfo-3.5.4-2.fc43.x86_64 libuv-debuginfo-1.51.0-2.fc43.x86_64 json-c-debuginfo-0.18-7.fc43.x86_64 libxml2-debuginfo-2.12.10-5.fc43.x86_64 zlib-ng-compat-debuginfo-2.3.3-2.fc43.x86_64 libcap-debuginfo-2.76-3.fc43.x86_64 lmdb-libs-debuginfo-0.9.34-1.fc43.x86_64 libcom_err-debuginfo-1.47.3-2.fc43.x86_64 keyutils-libs-debuginfo-1.6.3-6.fc43.x86_64 xz-libs-debuginfo-5.8.1-4.fc43.x86_64 libselinux-debuginfo-3.9-5.fc43.x86_64 pcre2-debuginfo-10.47-1.fc43.x86_64
The backtrace at the time of the crash is as follows:
(gdb) bt
#0 0x000055555561bfdb in dst_context_create (key=0x7fffe0001010, mctx=0x555555be7e20, category=DNS_LOGCATEGORY_DNSSEC, useforsigning=0x0, dctxp=0x7fffecdf7510) at lib/dns/dst_api.c:274
#1 0x0000555555613652 in dns_dnssec_verify (name=0x7fffe011e4f8, set=0x7fffecdf8a68, key=0x7fffe0001010, ignoretime=0x0, mctx=0x555555be7e20, sigrdata=0x7fffecdf7da0, wild=0x0) at lib/dns/dnssec.c:419
#2 0x00005555556152d4 in dns_dnssec_signs (rdata=0x7fffecdf8160, name=0x7fffe011e4f8, rdataset=0x7fffecdf8a68, sigrdataset=0x7fffecdf8ad8, ignoretime=0x0, mctx=0x555555be7e20) at lib/dns/dnssec.c:1037
#3 0x0000555555615063 in dns_dnssec_selfsigns (rdata=0x7fffecdf8160, name=0x7fffe011e4f8, rdataset=0x7fffecdf8a68, sigrdataset=0x7fffecdf8ad8, ignoretime=0x0, mctx=0x555555be7e20) at lib/dns/dnssec.c:1003
#4 0x0000555555825e4a in check_dnskey_sigs (vctx=0x7fffecdf8a30, dnskey=0x7fffecdf8140, keyrdata=0x7fffecdf8160, is_ksk=0x0) at lib/dns/zoneverify.c:1396
#5 0x0000555555826615 in check_dnskey (vctx=0x7fffecdf8a30) at lib/dns/zoneverify.c:1560
#6 0x00005555558278c7 in dns_zoneverify_dnssec (zone=0x555555e42980, db=0x7fffe011e4e0, ver=0x7fffe011eaa0, origin=0x7fffe011e4f8, secroots=0x555555ef33b0, mctx=0x555555be7e20, ignore_kskflag=0x1, keyset_kskonly=0x0,
report=0x55555580e6ca <dnssec_report at lib/dns/zone.c:19895>) at lib/dns/zoneverify.c:1908
#7 0x000055555581ff7c in dns_zone_verifydb (zone=0x555555e42980, db=0x7fffe011e4e0, ver=0x0) at lib/dns/zone.c:23876
#8 0x00005555555ce51e in axfr_apply_done (arg=0x7fffe011f990) at lib/dns/xfrin.c:388
#9 0x00005555555cd405 in isc__after_work_cb (req=0x7fffe011f9b0, status=0x0) at lib/isc/work.c:47
#10 0x00007ffff7dd64d1 in uv.work_done () from /lib64/libuv.so.1
#11 0x00007ffff7dcc60e in uv.async_io () from /lib64/libuv.so.1
#12 0x00007ffff7deb71e in uv.io_poll () from /lib64/libuv.so.1
#13 0x00007ffff7dd69e2 in uv_run () from /lib64/libuv.so.1
#14 0x00005555555aaeeb in loop_thread (arg=0x555555b37ef0) at lib/isc/loop.c:325
#15 0x00005555555c3ea2 in thread_body (wrap=0x555555bd6a80) at lib/isc/thread.c:87
#16 0x00005555555c3ef9 in thread_run (wrap=0x555555bd6a80) at lib/isc/thread.c:104
#17 0x00007ffff7436464 in start_thread () from /lib64/libc.so.6
#18 0x00007ffff74b95ec in __clone3 () from /lib64/libc.so.6
Looking at the register state, the %rax register is assigned to NULL and is dereferenced while loading another value into the %rax register.
(gdb) h
-=[registers]=-
[rax: 0x0000000000000000] [rbx: 0x00007fffecdf9730] [rcx: 0x0000000000000000]
[rdx: 0x0000000000000006] [rsi: 0x0000555555be7e20] [rdi: 0x00007fffe0001010]
[rsp: 0x00007fffecdf7440] [rbp: 0x00007fffecdf74a0] [ pc: 0x000055555561bfdb]
[ r8: 0x00007fffecdf7510] [ r9: 0x00007fffecdf7da0] [r10: 0x0000000000000145]
[r11: 0x00007ffff7fbd000] [r12: 0x0000000000000000] [r13: 0x00007ffff7dca880]
[r14: 0x0000555555b37fb8] [r15: 0x0000000000000000] [efl: 0x00010246]
[flags: +ZF -SF -OF -CF -DF +PF -AF +IF RF R1]
-=[stack]=-
0x7fffecdf7440 | 00007fffecdf7510 00000006e00fc700 | .u..............
0x7fffecdf7450 | 0000555555be7e20 00007fffe0001010 | ~.UUU..........
0x7fffecdf7460 | 00007fffe00fc785 00007fffa4001176 | ........v.......
0x7fffecdf7470 | 00007fffecdf74a0 00005555556799ed | .t........gUUU..
0x7fffecdf7480 | 00007fffecdf75e8 00007fffe011e4f8 | .u..............
0x7fffecdf7490 | 00007fffe00dcc40 0000000d69dff817 | @..........i....
0x7fffecdf74a0 | 00007fffecdf7d10 0000555555613652 | .}......R6aUUU..
0x7fffecdf74b0 | 0000000000000000 0000000000000000 | ................
-=[disassembly]=-
0x55555561bfcb <dst_context_create+178 at lib/dns/dst_api.c:272>: call 0x55555558dcc4 <isc_assertion_failed at lib/isc/assertions.c:47>
0x55555561bfd0 <dst_context_create+183 at lib/dns/dst_api.c:274>: mov -0x48(%rbp),%rax
0x55555561bfd4 <dst_context_create+187 at lib/dns/dst_api.c:274>: mov 0x128(%rax),%rax
=> 0x55555561bfdb <dst_context_create+194 at lib/dns/dst_api.c:274>: mov (%rax),%rax
0x55555561bfde <dst_context_create+197 at lib/dns/dst_api.c:274>: test %rax,%rax
0x55555561bfe1 <dst_context_create+200 at lib/dns/dst_api.c:274>: jne 0x55555561bfed <dst_context_create+212 at lib/dns/dst_api.c:277>
0x55555561bfe3 <dst_context_create+202 at lib/dns/dst_api.c:275>: mov $0xc0,%eax
(gdb) i r rax
rax 0x0 0x0
The relevant lines of code are:
(gdb) info line
Line 279 of "lib/dns/dst_api.c" is at address 0x55555561c004 <dst_context_create+235 at lib/dns/dst_api.c:281> but contains no code.
(gdb) l *$pc
0x55555561bfdb is in dst_context_create (lib/dns/dst_api.c:274).
269
270 REQUIRE(VALID_KEY(key));
271 REQUIRE(mctx != NULL);
272 REQUIRE(dctxp != NULL && *dctxp == NULL);
273
274 if (key->func->createctx == NULL) {
275 return DST_R_UNSUPPORTEDALG;
276 }
277 if (key->keydata.generic == NULL) {
278 return DST_R_NULLKEY;
This advisory comes with two proof-of-concepts. The first proof-of-concept, poc.zone.py, can be used to generate a signed-zone file which can then be used with the dnssec-verify(1) binary to trigger the dereference.
$ python poc.zone.py --domain example.com
[+] Wrote crafted zone to example.com.signed
Valid DNSKEY: alg=15 (Ed25519) keytag=3631
Crafted DNSKEY: alg=39 keytag=5159
trust-anchors {
"example.com." static-key 257 3 15 "yTbkzH2GEJVmp4kZw+5pmLfBmU4K5qUemj2JR/yij3o=";
};
[*] To crash dnssec-verify:
dnssec-verify -o example.com example.com.signed
Once the signed zone has been created, the following command will trigger the crash.
$ dnssec-verify -o example.com example.com.signed
Loading zone 'example.com' from file 'example.com.signed'
Segmentation fault (core dumped) dnssec-verify -o example.com example.com.signed
The second proof-of-concept runs an authoritative domain name server which can be mirrored by named(8) to trigger the vulnerability. An example configuration file that contacts the rogue domain name server bound to localhost is included to simplify the demonstration of the denial-of-service remotely.
To run the server, specify the address to bind to along with the domain as in the following output:
$ python poc.server.py --listen 127.0.0.1 --domain example.com
=================================================================
BIND9 DNSSEC NULL-deref PoC — Rogue Authoritative Server
=================================================================
Zone: example.com
Listen: 127.0.0.1:53 (UDP+TCP)
Valid DNSKEY: alg=15 (Ed25519) keytag=42688
Crafted DNSKEY: alg=39 keytag=5159 flags=0x1100
── named.conf ──
trust-anchors {
"example.com." static-key 257 3 15 "0YaB4tu/JvTwUhtRyzFJ5eRZ9EPJSNKPfye2svdvihc=";
};
zone "example.com" {
type mirror;
masters { 127.0.0.1; };
};
Waiting for queries...
Once the server is running, then named(8) can be started using the provided configuration to mirror from the domain-server bound to 127.0.0.1:53. It is worth noting that the provided configuration expects a user-writable directory named “./cache” in the current directory.
$ named -c poc.conf -g
2026-04-15T16:26:18.325-05:00 starting BIND 9.21.21 (Development Release) <id:ec2e1ce>
...
2026-04-15T16:26:18.325-05:00 compiled with OpenSSL version: OpenSSL 3.5.4 30 Sep 2025
2026-04-15T16:26:18.325-05:00 linked to OpenSSL version: OpenSSL 3.5.4 30 Sep 2025
2026-04-15T16:26:18.325-05:00 compiled with libuv version: 1.51.0
2026-04-15T16:26:18.325-05:00 linked to libuv version: 1.51.0
2026-04-15T16:26:18.325-05:00 compiled with liburcu-membarrier version: 0.15.6
2026-04-15T16:26:18.325-05:00 compiled with libnghttp2 version: 1.66.0
2026-04-15T16:26:18.325-05:00 linked to libnghttp2 version: 1.66.0
2026-04-15T16:26:18.325-05:00 compiled with libxml2 version: 2.12.10
2026-04-15T16:26:18.325-05:00 linked to libxml2 version: 21210
2026-04-15T16:26:18.325-05:00 compiled with json-c version: 0.18
2026-04-15T16:26:18.325-05:00 linked to json-c version: 0.18
2026-04-15T16:26:18.325-05:00 compiled with zlib version: 1.3.1.zlib-ng
2026-04-15T16:26:18.325-05:00 linked to zlib version: 1.3.1.zlib-ng
2026-04-15T16:26:18.325-05:00 ----------------------------------------------------
2026-04-15T16:26:18.325-05:00 BIND 9 is maintained by Internet Systems Consortium,
2026-04-15T16:26:18.325-05:00 Inc. (ISC), a non-profit 501(c)(3) public-benefit
2026-04-15T16:26:18.325-05:00 corporation. Support and training for BIND 9 are
2026-04-15T16:26:18.325-05:00 available at https://www.isc.org/support
2026-04-15T16:28:45.140-05:00 ----------------------------------------------------
...
2026-04-15T16:28:45.144-05:00 DNSSEC algorithms: RSASHA256 RSASHA512 ECDSAP256SHA256 ECDSAP384SHA384 ED25519 ED448 RSASHA256OID RSASHA512OID
2026-04-15T16:28:45.144-05:00 DS algorithms: SHA-1 SHA-256 SHA-384
2026-04-15T16:28:45.144-05:00 HMAC algorithms: HMAC-MD5 HMAC-SHA1 HMAC-SHA224 HMAC-SHA256 HMAC-SHA384 HMAC-SHA512
2026-04-15T16:28:45.144-05:00 TKEY mode 2 support (Diffie-Hellman): no
2026-04-15T16:28:45.144-05:00 TKEY mode 3 support (GSS-API): yes
...
2026-04-15T16:28:45.173-05:00 168: 'notify explicit;' will be used for mirror zone 'example.com'
2026-04-15T16:28:45.173-05:00 95: 'max-cache-size 90%' - setting to 43230MB (out of 48034MB)
2026-04-15T16:28:45.175-05:00 using built-in root key for view _default
2026-04-15T16:28:45.175-05:00 set up managed keys zone for view _default, file 'managed-keys.bind'
2026-04-15T16:28:45.182-05:00 not using config file logging statement for logging due to -g option
...
2026-04-15T16:28:45.185-05:00 managed-keys-zone: loaded serial 3
2026-04-15T16:28:45.192-05:00 all zones loaded
2026-04-15T16:28:45.193-05:00 FIPS mode is disabled
2026-04-15T16:28:45.193-05:00 running
...
2026-04-15T16:28:45.705-05:00 zone example.com/IN: Transfer started.
2026-04-15T16:28:45.713-05:00 0x7f32c8003930: transfer of 'example.com/IN' from 127.0.0.1#53: connected using 127.0.0.1#53
Segmentation fault (core dumped) named -c poc.conf -g
algorithm_status() check in frombuffer() (dst_api.c) outside of the if (isc_buffer_remaininglength(source) > 0) conditional so that unsupported algorithms are always rejected regardless of remaining buffer length.key->func before returning successfully from frombuffer().dnssec-validation no;) at the cost of losing DNSSEC protection, or by filtering inbound DNSKEY records with the EXTENDED flag bit set at the network level.Vendor Notes: Vendor advised they have analyzed and fixed the issue. They are not treating this as a security issue.
2026-04-29 - Initial Vendor Contact
2026-04-29 - Vendor Disclosure
2026-05-05 - Vendor Patch Release
2026-06-12 - Public Release
Ali Rizvi-Santiago of Cisco Talos
This vulnerability has not been disclosed and cannot be viewed at this time.