Predictable DNS Transaction IDs Enable Cache Poisoning in Built-in Resolver. CVE-2026-28810#10864
Merged
RaimoNiskanen merged 8 commits intoApr 7, 2026
Conversation
According to established practice, RFC 6056, use a random transaction ID for DNS queries, and also a random source (bind) port number. This is established practice to minimize the possibility for a succesful DNS cache poisoning attack through spoofing UDP answer packets. The random number generator that is used is `crypto:rand_uniform/2`, which is deprecated for outdated reasons. In really old, now obsolete, libcrypto versions the function was not cryptographically strong, but that should no longer be the case. For the `crypto` random number generator to be available, the Crypto application has to be loaded, and if not this code falls back to the legacy non-random behaviour. This should not be a problem since any Erlang node that is installed in an exposed environment should have the Crypto application loaded, for example to run the SSL application. This can also be done explicitly with `application:load(crypto)`. If the performance penalty for randomization is too much for an Erlang installation on a network protected from DNS spoofing, there is a boolean `inet` resolver option `random` that can be set to `false` to restore the legacy behaviour. To randomize ports, an internal port number argument of -1, instead of 0, is used by the internal function `inet:bind/3` to do 5 attempts to bind on truly random port numbers in the range 1024 .. 65535, then fallback to letting the OS pick an ephemeral port. A modern OS should pick random epemeral ports, but the range is smaller and the randomness not documented. This approach is a compromise that should make the port number cryptographically random in all practical cases with a fallback that should be very hard to exploit. Transaction ID:s are simply random 16 bits. There is no attempt to avoid duplicates. The probability for duplicates in both transaction ID and port number should be less than 1/(2^31), and if that should happen, the query domain, class and type, also have to match for the reply to be succesfully decoded. Since the probability is so low this is not a practically exploitable vulnerability, and if the bogus reply is not an attack it is a slightly old valid reply so no harm is done. This commit also rewrites the `inet_dns` decoding code to check the received reply header in steps, starting with the transaction ID, and bail out immediately when a reply does not match the query. The old code decoded the whole reply packet first before checking any field. This code is much more efficient in the precense of bogus reply packets.
Use two ETS set tables to record used transaction IDs. If a generated random ID is already in the tables; reject and generate a new. One table is the New and the other the Old. When the Old table is oolder than 60 seconds, or the New table has more than 8192 IDs, wipe the Old table and retire the New into being the Old.
This reverts commit ee70b41adfa64ff442b64d87fce9ad5e8a32311c. Avoiding duplicate IDs is deemed not be worth the effort, since it adds typically five ETS lookup/member and insert operations on top on the single NIF call to libcrypto. The probability for a collision is, as explained in the commit comment implementing the random DNS transaction IDs, too low to be a usable exploit, and a collision should also not be harmful in normal use.
…l/inet_res-id-27/OTP-20037 * raimo/kernel/inet_res-id-26/OTP-20037: Revert "Avoid duplicate DNS transaction IDs" Avoid duplicate DNS transaction IDs Randomize `inet_res` transaction ID and source port number
…l/inet_res-id-28/OTP-20037 * raimo/kernel/inet_res-id-27/OTP-20037: Revert "Avoid duplicate DNS transaction IDs" Avoid duplicate DNS transaction IDs Randomize `inet_res` transaction ID and source port number
* Hide the randomized port number -1 internal feature better, so that it does not show in the type spec of `gen_udp:open/2`. Suppress the Dialyzer warnings that follows from that. * Mention RFC 5452 in the RFC list. * Explain in a code comment why we use a deprecated `crypto` function. * Fix inet:bind_random to actually do a final attempt with port 0. Reduce the number of random attempts to 3 since the "always" succeed anyway on a "normal" machine. * Fix SCTP modules to handle port number like the TCP and UDP modules.
…l/inet_res-id-27/OTP-20037 * raimo/kernel/inet_res-id-26/OTP-20037: Update after feedback
…l/inet_res-id-28/OTP-20037 * raimo/kernel/inet_res-id-27/OTP-20037: Update after feedback
Contributor
CT Test Results 2 files 74 suites 1h 12m 4s ⏱️ Results for commit e2fd738. ♻️ This comment has been updated with latest results. To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass. See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally. Artifacts
// Erlang/OTP Github Action Bot |
bjorng
reviewed
Mar 18, 2026
Contributor
|
Also remember to remove this line: |
RaimoNiskanen
force-pushed
the
raimo/kernel/inet_db-catch/OTP-20037
branch
from
March 18, 2026 13:47
46a1988 to
e2fd738
Compare
bjorng
previously approved these changes
Mar 18, 2026
RaimoNiskanen
force-pushed
the
raimo/kernel/inet_db-catch/OTP-20037
branch
from
April 7, 2026 06:05
e2fd738 to
36f23c9
Compare
inet_db
Contributor
Author
This was referenced Jun 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The documentation is updated to clarify that inet_res should only be used in trusted networks and with trusted recursive resolvers.
The implementation is also improved to use strong random DNS transaction IDs and source ports for every DNS transaction. This should give ample protection against brute forcing fake DNS replies, but it still does not protect against, for example, an adversary in the path of the DNS transaction that can observe the random values before faking malicious replies, an attack known as CAPEC-598: DNS Spoofing.