Antonius (w1sdom)
Website: bluedragonsec.com
GitHub: bluedragonsecurity
- Project: LiteDNS
- Project URL: https://github.com/TanishqNanavati/LiteDNS
- LiteDNS
- Out-of-bounds Read leading to Denial of Service (DoS)
- CWE-125: Out-of-bounds Read
src/dns_parser.c→decode_domain()src/dns_parser.c→parse_question()src/dns_response.c→build_response()
LiteDNS before a fix for this issue.
If exact tagged versions are unavailable, use:
current upstream source / latest tested revision at the time of discovery
High for availability in exposed deployments.
Suggested CVSS v3.1:
7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
LiteDNS contains an out-of-bounds read in DNS question parsing that can be triggered after normal program initialization, once the service begins listening for DNS requests. A remote unauthenticated attacker can send a crafted truncated DNS query containing a malformed compressed domain pointer, causing the server to read beyond the received packet boundary and crash, resulting in denial of service.
The vulnerability exists in the DNS domain-name parsing logic. The function decode_domain() reads attacker-controlled bytes from the incoming packet without validating that the current offset remains within the received packet length. In particular, when the parser encounters a byte matching the DNS compression-pointer pattern ((len & 0xC0) == 0xC0), it immediately reads buff[offset + 1] to construct the pointer target without checking whether the second pointer byte is still present in the received packet.
The issue is further exposed because parse_question() reads qtype and qclass from buff + offset and buff + offset + 2 without validating that sufficient bytes remain, and build_response() receives query_len from recvfrom() but does not use query_len to enforce parsing bounds.
LiteDNS requires interactive record initialization before entering its UDP receive loop. After initialization is completed and the server starts listening on port 5353, a remote attacker can send a malformed DNS request that triggers an out-of-bounds read and crashes the LiteDNS process.
-
In
main(), LiteDNS initializes DNS records interactively before binding and listening on UDP port5353. -
After initialization is complete, LiteDNS receives a UDP DNS query into a stack-allocated buffer:
unsigned char query[BUFF_SIZE], response[BUFF_SIZE];
int len = recvfrom(sockfd, query, BUFF_SIZE, 0, ...);
int res_len = build_response(query, len, response, BUFF_SIZE);build_response()acceptsquery_lenbut does not use it to validate parser accesses. It directly calls:
int offset = parse_question(query, sizeof(DNSHeader), qname, &qtype, &qclass);parse_question()callsdecode_domain()and then immediately reads:
*qtype = ntohs(*(uint16_t *)(buff + offset));
*qclass = ntohs(*(uint16_t *)(buff + offset + 2));with no verification that at least 4 bytes remain.
decode_domain()performs:
len = buff[offset];and, for compression pointers:
offset = ((len & 0x3F) << 8) | buff[offset + 1];without checking whether offset and offset + 1 are within the received packet.
- A truncated packet ending in a single fake compression-pointer byte causes the parser to read beyond the valid packet boundary. The resulting attacker-influenced offset may then trigger an additional invalid read on a later iteration, leading to a segmentation fault.
Improper bounds checking on attacker-controlled offsets during DNS domain-name parsing and compression-pointer handling.
Remote unauthenticated denial of service after the service has been initialized and is listening for requests. A single crafted UDP DNS packet can crash the LiteDNS server process.
- Remote
- Network-accessible
- Unauthenticated
- Via UDP DNS query
The operator must first complete LiteDNS record initialization so that the program enters its network-listening state.
12 34 01 00 00 01 00 00 00 00 00 00 02 41 42 FF
- Total packet size: 16 bytes
- DNS header size: 12 bytes
- The remaining 4 bytes form the beginning of the Question section:
02 41 42 FF QDCOUNT = 10x02is the label length0x41 0x42are the label bytes ("AB")0xFFis interpreted as the beginning of a DNS compression pointerdecode_domain()then readsbuff[offset + 1]even though the packet ends there
Query from 127.0.0.1:53626 (16 bytes)
AddressSanitizer:DEADLYSIGNAL
=================================================================
==1021829==ERROR: AddressSanitizer: SEGV on unknown address 0x10007fff82ae
==1021829==The signal is caused by a READ memory access.
#0 0x20348f in decode_domain src/dns_parser.c:9
#1 0x203e87 in parse_question src/dns_parser.c:57
#2 0x2060bd in build_response src/dns_response.c:75
#3 0x2066d2 in main /var/root/LiteDNS/main.c:53
#4 0x7ffff662a60f in __libc_start_call_main
#5 0x7ffff662a6bf in __libc_start_main@GLIBC_2.2.5
#6 0x203354 in _start (/var/root/LiteDNS/dns-server+0x203354)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV src/dns_parser.c:9 in decode_domain
==1021829==ABORTING
- Operating System: CentOS Stream 9
- Architecture: x86_64
- Kernel: Linux
5.14.0-635.el9.x86_64 - Working Directory During Testing:
/var/root/LiteDNS - Build Configuration: AddressSanitizer-enabled build
- Build LiteDNS with AddressSanitizer enabled.
- Start LiteDNS.
- Add at least one DNS record during the interactive setup.
- Enter
doneso the program completes initialization. - Wait until LiteDNS prints:
DNS Server listening on port 5353
- Send the crafted 16-byte UDP DNS packet to port
5353. - Observe the crash in
decode_domain().
main.c: interactive record initialization occurs before the server starts listeningmain.c: stack-allocatedquerybuffer receives attacker-controlled UDP input and passeslentobuild_response()dns_response.c:build_response()ignoresquery_lenduring parsingdns_parser.c:decode_domain()readsbuff[offset]andbuff[offset + 1]without validating packet boundsdns_parser.c:parse_question()readsqtypeandqclasswithout verifying remaining length
- Pass packet length (or end offset) into
decode_domain()andparse_question() - Validate every read against
query_lenbefore dereferencing - Reject truncated compression pointers
- Reject out-of-range compression offsets
- Verify that
qtypeandqclassare fully present before reading - Validate
q_lenbeforememcpy()inbuild_response()
Besides the confirmed out-of-bounds read in decode_domain(), build_response() derives q_len from a parser-controlled offset and then performs:
memcpy(response + sizeof(DNSHeader), query + sizeof(DNSHeader), q_len)without validating that q_len is within the received packet length. This may create additional unsafe behavior once parsing state is corrupted.
- Primary: CWE-125 Out-of-bounds Read
- Impact: Denial of Service
Discovered and reported by Antonius (w1sdom)
Website: https://bluedragonsec.com
GitHub: https://github.com/bluedragonsecurity/
- March 5, 2026 - Vulnerability discovered
- March 6, 2026 - Vendor contacted privately
- Email:
tanishqnanavati3@gmail.com - GitHub: https://github.com/TanishqNanavati
At the time of reporting, the confirmed impact is denial of service via crash. This report does not claim code execution.
https://medium.com/@w1sdom/litedns-out-of-bounds-read-in-dns-name-parsing-leads-to-denial-of-service-e4a41a7efa49 https://github.com/bluedragonsecurity/LiteDNS_out_of_bounds_read_vulnerability