Denial of service, medium severity.
talkd/talkd.c:116
talkd accepted an unauthenticated UDP request, copied the reply destination from attacker-controlled request.ctl_addr, and sent a CTL_RESPONSE to that address. A remote client could set ctl_addr to an arbitrary IPv4 victim and cause the daemon to emit UDP traffic to the victim, making talkd a UDP reflector.
Reported by Swival Security Scanner: https://swival.dev
Confidence: certain.
talkd receives attacker-controlled UDP datagrams on its standard input socket.
The issue was reproduced.
A client sends a full-size CTL_MSG to ntalkd with request.ctl_addr.sa_family set to network-order AF_INET and the embedded IPv4 address and port set to a victim.
Observed data flow:
talkd/talkd.c:97reads the attacker-controlled datagram directly intorequest.talkd/talkd.c:100only requires the datagram to be exactlysizeof(request).talkd/talkd.c:111copiesrequest.ctl_addrintoctl_addr.talkd/talkd.c:112rewrites only the family from network to host order.talkd/talkd.c:113setssa_len, preserving attacker-controlled IPv4 destination bytes.talkd/process.c:91compares the packet source address withmp->ctl_addr, buttalkd/process.c:100only logs when they differ and continues.talkd/talkd.c:120sends theCTL_RESPONSEtoctl_addr, not to therecvfromsource stored inresponse.addr.
Impact: an unauthenticated remote client can repeatedly cause the daemon to send UDP responses to an arbitrary IPv4 victim.
The daemon already obtains the actual packet source through recvfrom into response.addr, but does not use that address for the reply. Instead, it trusts request.ctl_addr, which is part of the attacker-controlled CTL_MSG payload.
The existing mismatch check in request processing is not a security control because it only logs when the packet source and embedded control address differ. It does not reject the request. Therefore, a spoofed embedded ctl_addr still reaches sendto.
Because UDP is connectionless and the sender can choose the embedded address without authentication, this creates a practical reflection primitive against arbitrary IPv4 targets.
Send replies to the source address returned by recvfrom, not to request.ctl_addr.
The patch changes the reply destination source from attacker-controlled request data to the socket address populated by recvfrom.
Before the patch:
memcpy(&ctl_addr, &request.ctl_addr, sizeof(ctl_addr));
ctl_addr.sa_family = ntohs(request.ctl_addr.sa_family);After the patch:
memcpy(&ctl_addr, &response.addr, sizeof(ctl_addr));response.addr is filled by recvfrom with the datagram source address. The existing AF_INET validation remains in place, but it now validates the real peer address used for the response rather than an attacker-selected address embedded in the payload.
This removes the reflection primitive because the daemon no longer sends responses to arbitrary addresses supplied inside CTL_MSG.
None
diff --git a/talkd/talkd.c b/talkd/talkd.c
index 79fe6dd..14e6cdb 100644
--- a/talkd/talkd.c
+++ b/talkd/talkd.c
@@ -108,8 +108,7 @@ main(int argc, char *argv[])
request.r_name[sizeof(request.r_name) - 1] = '\0';
request.r_tty[sizeof(request.r_tty) - 1] = '\0';
- memcpy(&ctl_addr, &request.ctl_addr, sizeof(ctl_addr));
- ctl_addr.sa_family = ntohs(request.ctl_addr.sa_family);
+ memcpy(&ctl_addr, &response.addr, sizeof(ctl_addr));
ctl_addr.sa_len = sizeof(ctl_addr);
if (ctl_addr.sa_family != AF_INET)
continue;