CVE-2026-83813
Twisted: DNS Response Spoofing in twisted.names Due to Missing Source Address Validation
Twisted is an event-based framework for internet applications, supporting Python 3.6+. Prior to 26.10.0rc1, twisted.names.dns.DNSDatagramProtocol accepts a UDP DNS response for a pending query when its 16-bit transaction ID matches but does not verify that the response came from the queried host and port. An attacker able to deliver a packet to the resolver's listening port with the correct transaction ID can inject a forged answer. The standard twisted.names.client.Resolver uses a new random source port for each query, increasing off-path attack complexity, while applications that reuse a DNSDatagramProtocol instance expose only the transaction-ID search space after the first query. A successful forgery can redirect hostname resolution and enable mail interception, server-side request forgery, or man-in-the-middle positioning. This issue is fixed in version 26.10.0rc1.
Attack profile
How easy it is to exploit
- Attack vector
- Adjacent network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- Low
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
3.1
Twisted: DNS Response Spoofing in twisted.names Due to Missing Source Address Validation
Twisted is an event-based framework for internet applications, supporting Python 3.6+. Prior to 26.10.0rc1, twisted.names.dns.DNSDatagramProtocol accepts a UDP DNS response for a pending query when its 16-bit transaction ID matches but does not verify that the response came from the queried host and port. An attacker able to deliver a packet to the resolver's listening port with the correct transaction ID can inject a forged answer. The standard twisted.names.client.Resolver uses a new random source port for each query, increasing off-path attack complexity, while applications that reuse a DNSDatagramProtocol instance expose only the transaction-ID search space after the first query. A successful forgery can redirect hostname resolution and enable mail interception, server-side request forgery, or man-in-the-middle positioning. This issue is fixed in version 26.10.0rc1.
- Record
CVE-2026-83813- CVSS 3.1
- 3.1 Low
CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N - Weakness
- CWE-346
- Updated
- 2026-11-02
EUVDEUVD-2026-109152
3.1
Twisted is an event-based framework for internet applications, supporting Python 3.6+. Prior to 26.10.0rc1, twisted.names.dns.DNSDatagramProtocol accepts a UDP DNS response for a pending query when its 16-bit transaction ID matches but does not verify that the response came from the queried host and port. An attacker able to deliver a packet to the resolver's listening port with the correct transaction ID can inject a forged answer. The standard twisted.names.client.Resolver uses a new random source port for each query, increasing off-path attack complexity, while applications that reuse a DNSDatagramProtocol instance expose only the transaction-ID search space after the first query. A successful forgery can redirect hostname resolution and enable mail interception, server-side request forgery, or man-in-the-middle positioning. This issue is fixed in version 26.10.0rc1.
- Record
EUVD-2026-109152- CVSS 3.1
- 3.1 Low
CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-jj36-9pvh-vj5r
3.1
Twisted: DNS Response Spoofing in twisted.names Due to Missing Source Address Validation
Summary
twisted.names.dns.DNSDatagramProtocol, the UDP transport used by twisted's DNS client (twisted.names.client.Resolver, createResolver, and anything built on it, including MX lookups in twisted.mail), accepts a DNS response for a pending query solely on the basis of the 16-bit transaction ID matching. It never checks that the response actually originated from the server the query was sent to. Any host that can deliver a UDP packet to the resolver's listening port with the correct transaction ID can inject a forged answer, independent of the real server's address. This is a materially weaker defense than standard DNS resolver practice (transaction ID plus source-address validation, typically also source-port rotation), and enables off-path DNS response spoofing / cache poisoning.
Details
DNSDatagramProtocol.datagramReceived (src/twisted/names/dns.py, line 3350) is:
def datagramReceived(self, data, addr):
"""
Read a datagram, extract the message in it and trigger the associated
Deferred.
"""
m = Message()
try:
m.fromStr(data)
except EOFError:
...
except ValueError as ex:
...
except BaseException:
...
if m.id in self.liveMessages:
d, canceller = self.liveMessages[m.id]
del self.liveMessages[m.id]
canceller.cancel()
try:
d.callback(m)
except BaseException:
log.err()
else:
if m.id not in self.resends:
self.controller.messageReceived(m, self, addr)
The addr parameter, the UDP source address of the datagram, is accepted but never compared against the address the corresponding query was sent to. The only check is m.id in self.liveMessages, and liveMessages is keyed purely by the transaction ID chosen in DNSMixin.pickID(). The listening socket is a plain listenUDP(0, self, maxPacketSize=512) (line 3348), not connected to the queried server, so the kernel does not filter by source address either; any host can deliver a UDP datagram to this socket.
The transaction ID itself is generated with secureRandom (randomSource(), line 174), so it is not predictable, but at 16 bits it is a 1-in-65536 space per attempt. twisted.names.client.Resolver, the standard, documented DNS client, mitigates brute-force guessing of that space by creating a brand-new DNSDatagramProtocol bound to a fresh, independently random source port (_connectedProtocol(), src/twisted/names/client.py line 206, using dns.randomSource()) for every individual query, and tearing the socket down once the query completes (_query(), line 253). This gives an off-path attacker roughly 32 bits of combined entropy (port and transaction ID) to guess per query issued through the standard client, not just the 16 bits of the transaction ID alone.
The missing source-address check is still a real gap for two reasons. First, it removes a defense-in-depth backstop that DNS resolver best practice (RFC 5452) treats as independent from port and ID randomization; an attacker who is on-path, shares a broadcast domain or NAT with the resolver or the real DNS server, or is otherwise able to observe or narrow the ephemeral port faces no additional barrier at all, since source-address validation is exactly the check that would stop them. Second, DNSDatagramProtocol is itself a public, documented class; code that constructs one directly and issues more than one query against the same instance, rather than going through client.Resolver's per-query protocol and port cycling, reuses the same source port for every query after the first, so guessing the 16-bit transaction ID alone is sufficient from that point on.
twisted.names.client.Resolver uses this same DNSDatagramProtocol class for every UDP query it issues, so every consumer of the standard twisted DNS client, including twisted.mail's MX resolution, is subject to the same unauthenticated accept logic in…
- Record
GHSA-jj36-9pvh-vj5r- CVSS 3.1
- 3.1 Low
CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N - Weakness
- CWE-346
- Updated
- 2026-11-02
NVD
3.1
Twisted is an event-based framework for internet applications, supporting Python 3.6+. Prior to 26.10.0rc1, twisted.names.dns.DNSDatagramProtocol accepts a UDP DNS response for a pending query when its 16-bit transaction ID matches but does not verify that the response came from the queried host and port. An attacker able to deliver a packet to the resolver's listening port with the correct transaction ID can inject a forged answer. The standard twisted.names.client.Resolver uses a new random source port for each query, increasing off-path attack complexity, while applications that reuse a DNSDatagramProtocol instance expose only the transaction-ID search space after the first query. A successful forgery can redirect hostname resolution and enable mail interception, server-side request forgery, or man-in-the-middle positioning. This issue is fixed in version 26.10.0rc1.
- Record
CVE-2026-83813- CVSS 3.1
- 3.1 Low
CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N - Weakness
- CWE-346
- Updated
- 2026-11-02
OSV
3.1
Twisted: DNS Response Spoofing in twisted.names Due to Missing Source Address Validation
Twisted is an event-based framework for internet applications, supporting Python 3.6+. Prior to 26.10.0rc1, twisted.names.dns.DNSDatagramProtocol accepts a UDP DNS response for a pending query when its 16-bit transaction ID matches but does not verify that the response came from the queried host and port. An attacker able to deliver a packet to the resolver's listening port with the correct transaction ID can inject a forged answer. The standard twisted.names.client.Resolver uses a new random source port for each query, increasing off-path attack complexity, while applications that reuse a DNSDatagramProtocol instance expose only the transaction-ID search space after the first query. A successful forgery can redirect hostname resolution and enable mail interception, server-side request forgery, or man-in-the-middle positioning. This issue is fixed in version 26.10.0rc1.
- Record
CVE-2026-83813- CVSS 3.1
- 3.1 Low
CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N - Weakness
- CWE-346
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-83813
3.1
Twisted is an event-based framework for internet applications, supporting Python 3.6+. Prior to 26.10.0rc1, twisted.names.dns.DNSDatagramProtocol accepts a UDP DNS response for a pending query when its 16-bit transaction ID matches but does not verify that the response came from the queried host and port. An attacker able to deliver a packet to the resolver's listening port with the correct transaction ID can inject a forged answer. The standard twisted.names.client.Resolver uses a new random source port for each query, increasing off-path attack complexity, while applications that reuse a DNSDatagramProtocol instance expose only the transaction-ID search space after the first query. A successful forgery can redirect hostname resolution and enable mail interception, server-side request forgery, or man-in-the-middle positioning. This issue is fixed in version 26.10.0rc1.
- Record
DEBIAN-CVE-2026-83813- CVSS 3.1
- 3.1 Low
CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N - Updated
- 2026-11-02
Show 5 moreShow less
OSVGHSA-jj36-9pvh-vj5r
3.1
Twisted: DNS Response Spoofing in twisted.names Due to Missing Source Address Validation
Summary
twisted.names.dns.DNSDatagramProtocol, the UDP transport used by twisted's DNS client (twisted.names.client.Resolver, createResolver, and anything built on it, including MX lookups in twisted.mail), accepts a DNS response for a pending query solely on the basis of the 16-bit transaction ID matching. It never checks that the response actually originated from the server the query was sent to. Any host that can deliver a UDP packet to the resolver's listening port with the correct transaction ID can inject a forged answer, independent of the real server's address. This is a materially weaker defense than standard DNS resolver practice (transaction ID plus source-address validation, typically also source-port rotation), and enables off-path DNS response spoofing / cache poisoning.
Details
DNSDatagramProtocol.datagramReceived (src/twisted/names/dns.py, line 3350) is:
def datagramReceived(self, data, addr):
"""
Read a datagram, extract the message in it and trigger the associated
Deferred.
"""
m = Message()
try:
m.fromStr(data)
except EOFError:
...
except ValueError as ex:
...
except BaseException:
...
if m.id in self.liveMessages:
d, canceller = self.liveMessages[m.id]
del self.liveMessages[m.id]
canceller.cancel()
try:
d.callback(m)
except BaseException:
log.err()
else:
if m.id not in self.resends:
self.controller.messageReceived(m, self, addr)
The addr parameter, the UDP source address of the datagram, is accepted but never compared against the address the corresponding query was sent to. The only check is m.id in self.liveMessages, and liveMessages is keyed purely by the transaction ID chosen in DNSMixin.pickID(). The listening socket is a plain listenUDP(0, self, maxPacketSize=512) (line 3348), not connected to the queried server, so the kernel does not filter by source address either; any host can deliver a UDP datagram to this socket.
The transaction ID itself is generated with secureRandom (randomSource(), line 174), so it is not predictable, but at 16 bits it is a 1-in-65536 space per attempt. twisted.names.client.Resolver, the standard, documented DNS client, mitigates brute-force guessing of that space by creating a brand-new DNSDatagramProtocol bound to a fresh, independently random source port (_connectedProtocol(), src/twisted/names/client.py line 206, using dns.randomSource()) for every individual query, and tearing the socket down once the query completes (_query(), line 253). This gives an off-path attacker roughly 32 bits of combined entropy (port and transaction ID) to guess per query issued through the standard client, not just the 16 bits of the transaction ID alone.
The missing source-address check is still a real gap for two reasons. First, it removes a defense-in-depth backstop that DNS resolver best practice (RFC 5452) treats as independent from port and ID randomization; an attacker who is on-path, shares a broadcast domain or NAT with the resolver or the real DNS server, or is otherwise able to observe or narrow the ephemeral port faces no additional barrier at all, since source-address validation is exactly the check that would stop them. Second, DNSDatagramProtocol is itself a public, documented class; code that constructs one directly and issues more than one query against the same instance, rather than going through client.Resolver's per-query protocol and port cycling, reuses the same source port for every query after the first, so guessing the 16-bit transaction ID alone is sufficient from that point on.
twisted.names.client.Resolver uses this same DNSDatagramProtocol class for every UDP query it issues, so every consumer of the standard twisted DNS client, including twisted.mail's MX resolution, is subject to the same unauthenticated accept logic in…
- Record
GHSA-jj36-9pvh-vj5r- CVSS 3.1
- 3.1 Low
CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N - Weakness
- CWE-346
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-83813
3.1
Twisted is an event-based framework for internet applications, supporting Python 3.6+. Prior to 26.10.0rc1, twisted.names.dns.DNSDatagramProtocol accepts a UDP DNS response for a pending query when its 16-bit transaction ID matches but does not verify that the response came from the queried host and port. An attacker able to deliver a packet to the resolver's listening port with the correct transaction ID can inject a forged answer. The standard twisted.names.client.Resolver uses a new random source port for each query, increasing off-path attack complexity, while applications that reuse a DNSDatagramProtocol instance expose only the transaction-ID search space after the first query. A successful forgery can redirect hostname resolution and enable mail interception, server-side request forgery, or man-in-the-middle positioning. This issue is fixed in version 26.10.0rc1.
- Record
UBUNTU-CVE-2026-83813- CVSS 3.1
- 3.1 Low
CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N - Updated
- 2026-11-05
OSVAZL-111419 No score
CVE-2026-83813 affecting package python-twisted 22.10.0-7
Twisted is an event-based framework for internet applications, supporting Python 3.6+. Prior to 26.10.0rc1, twisted.names.dns.DNSDatagramProtocol accepts a UDP DNS response for a pending query when its 16-bit transaction ID matches but does not verify that the response came from the queried host and port. An attacker able to deliver a packet to the resolver's listening port with the correct transaction ID can inject a forged answer. The standard twisted.names.client.Resolver uses a new random source port for each query, increasing off-path attack complexity, while applications that reuse a DNSDatagramProtocol instance expose only the transaction-ID search space after the first query. A successful forgery can redirect hostname resolution and enable mail interception, server-side request forgery, or man-in-the-middle positioning. This issue is fixed in version 26.10.0rc1.
- Record
AZL-111419- Updated
- 2026-11-05
OSVCLSA-2026-1793702577 No score
TuxCare security update for twisted (1 CVE)
TuxCare rebuilt twisted to address 1 CVE. Fixed in 20.3.0.post8+tuxcare.
- Record
CLSA-2026-1793702577- Updated
- 2026-11-03
OSVopenSUSE-SU-2026:12381-1 No score
python-Twisted-doc-26.10.0-1.1 on GA media
These are all security issues fixed in the python-Twisted-doc-26.10.0-1.1 package on the GA media of openSUSE Tumbleweed.
- Record
openSUSE-SU-2026:12381-1- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
twistedPyPI |
through 26.4.0 | 26.10.0rc1 |
python-twistedAzure Linux:3 |
through 22.10.0-7 | None known |
twistedTuxCare:PyPI |
20.3.0 up to 20.3.0.post8+tuxcare, plus listed versions 20.3.0.post4+tuxcare20.3.0.post5+tuxcare20.3.0.post6+tuxcare20.3.0.post7+tuxcare |
20.3.0.post8+tuxcare |
twistedDebian:12 |
All versions, plus listed versions 22.4.0-422.4.0-4+deb12u123.10.0-123.10.0-224.10.0-1 Show 20 moreShow less24.10.0-224.11.0-124.3.0-124.3.0-224.3.0-324.7.0-124.7.0-224.7.0-325.5.0-125.5.0-225.5.0-325.5.0-425.5.0-526.10.0-126.4.0-126.4.0-226.4.0-326.4.0-426.4.0-526.4.0-6 |
None known |
twistedDebian:13 |
All versions, plus listed versions 24.11.0-125.5.0-125.5.0-225.5.0-325.5.0-4 Show 8 moreShow less25.5.0-526.10.0-126.4.0-126.4.0-226.4.0-326.4.0-426.4.0-526.4.0-6 |
None known |
twistedDebian:14 |
before 26.10.0-1, plus listed versions 24.11.0-125.5.0-125.5.0-225.5.0-325.5.0-4 Show 7 moreShow less25.5.0-526.4.0-126.4.0-226.4.0-326.4.0-426.4.0-526.4.0-6 |
26.10.0-1 |
twistedPyPI |
before 26.10.0rc1, plus listed versions 1.0.11.0.31.0.41.0.51.0.6 Show 45 moreShow less1.0.71.1.01.1.11.2.010.0.010.1.010.2.011.0.011.1.012.0.012.1.012.2.012.3.013.0.013.1.013.2.014.0.014.0.114.0.215.0.015.1.015.2.015.2.115.3.015.4.015.5.016.0.016.1.016.1.116.2.016.3.016.3.116.3.216.4.016.4.116.5.016.5.0rc116.5.0rc216.6.016.6.0rc116.7.0rc116.7.0rc217.1.017.1.0rc117.5.0 |
26.10.0rc1 |
twistedUbuntu:Pro:14.04:LTS |
All versions, plus listed versions 13.0.0-1ubuntu113.2.0-1ubuntu113.2.0-1ubuntu1.213.2.0-1ubuntu1.2+esm113.2.0-1ubuntu1.2+esm213.2.0-1ubuntu1.2+esm3 |
None known |
twistedUbuntu:Pro:16.04:LTS |
All versions, plus listed versions 15.2.1-1ubuntu215.5.0-115.5.0-215.5.0-2ubuntu115.5.0-4 Show 9 moreShow less16.0.0~pre1-116.0.0~pre1-1ubuntu116.0.0~pre1-2ubuntu116.0.0~pre1-316.0.0-116.0.0-1ubuntu0.216.0.0-1ubuntu0.416.0.0-1ubuntu0.4+esm116.0.0-1ubuntu0.4+esm2 |
None known |
twistedUbuntu:Pro:18.04:LTS |
All versions, plus listed versions 16.6.0-2ubuntu317.9.0-117.9.0-217.9.0-2ubuntu0.117.9.0-2ubuntu0.317.9.0-2ubuntu0.3+esm117.9.0-2ubuntu0.3+esm2 |
None known |
Show 6 moreShow less
| Package | Affected | Fixed |
|---|---|---|
twistedUbuntu:20.04:LTS |
All versions, plus listed versions 18.9.0-3ubuntu118.9.0-518.9.0-618.9.0-6build118.9.0-6ubuntu1 Show 7 moreShow less18.9.0-818.9.0-1118.9.0-11ubuntu0.20.04.118.9.0-11ubuntu0.20.04.218.9.0-11ubuntu0.20.04.318.9.0-11ubuntu0.20.04.418.9.0-11ubuntu0.20.04.5 |
None known |
twistedUbuntu:22.04:LTS |
All versions, plus listed versions 20.3.0-7ubuntu120.3.0-7ubuntu322.1.0-2ubuntu222.1.0-2ubuntu2.122.1.0-2ubuntu2.3 Show 5 moreShow less22.1.0-2ubuntu2.422.1.0-2ubuntu2.522.1.0-2ubuntu2.622.1.0-2ubuntu2.722.1.0-2ubuntu2.8 |
None known |
twistedUbuntu:24.04:LTS |
All versions, plus listed versions 22.4.0-422.4.0-4ubuntu123.10.0-224.3.0-124.3.0-1ubuntu0.124.3.0-1ubuntu0.224.3.0-1ubuntu0.3 |
None known |
twistedUbuntu:26.10 |
All versions, plus listed versions 25.5.0-526.4.0-426.4.0-4ubuntu0.1 |
None known |
twistedUbuntu:26.04:LTS |
All versions, plus listed versions 24.11.0-125.5.0-425.5.0-525.5.0-5ubuntu0.125.5.0-5ubuntu0.2 |
None known |
python-TwistedopenSUSE:Tumbleweed From an advisory covering 4 CVEs |
before 26.10.0-1.1 | 26.10.0-1.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected | ||
| affected | OSV | |
| affected, facts | OSV | |
| affected, aliases | OSV | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-83813 is a low vulnerability (CVSS 3.1) in Twisted, published October 23, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.11%, the 1st percentile. Fixed in twisted 26.10.0rc1.
References
github.com/twisted/twisted/security/advisories/GHSA-jj36-9pvh-vj5r
github.com/twisted/twisted/pull/12700
github.com/twisted/twisted/commit/b53e1c3477c209b5609a1c74cf9e48373b5879ac
github.com/twisted/twisted/releases/tag/twisted-26.10.0rc1
nvd.nist.gov/vuln/detail/CVE-2026-83813
github.com/advisories/GHSA-jj36-9pvh-vj5r
Show 9 moreShow less
github.com/CVEProject/cvelistV5/tree/main/cves/2026/83xxx/CVE-2026-83813.json
security-tracker.debian.org/tracker/CVE-2026-83813
github.com/twisted/twisted
ubuntu.com/security/CVE-2026-83813
cve.org/CVERecord?id=CVE-2026-83813
suse.com/security/cve/CVE-2026-115776
suse.com/security/cve/CVE-2026-115778
suse.com/security/cve/CVE-2026-115877
suse.com/security/cve/CVE-2026-83813
Left out here: 61 versions and the rest of 2 source descriptions. The JSON record has them.