Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <87jyqju0hf.fsf@gmail.com>
Date: Fri, 24 Jul 2026 22:01:00 -0700
From: Collin Funk <collin.funk1@...il.com>
To: oss-security@...ts.openwall.com
Cc: Tristan <TristanInSec@...il.com>, Simon Josefsson <simon@...efsson.org>
Subject: GNU Inetutils talkd buffer overflow with long DNS names.

# GNU Inetutils Security Advisory: talkd buffer overflow with long DNS names

## History

GNU Inetutils distributes a 'talkd' program, which uses the ntalk
protocol to communicate over the network. Like much of GNU Inetutils,
this implementation was derived from 4.4BSD sources. From what I can
tell, the issue described in this email only applies to GNU Inetutils
and was introduced by a change on 2001-10-25 [1]. The first release with
that change was GNU inetutils-1.4.0, released on 2002-07-31. Regardless,
I reported the issue privately to the distros mailing list on
2026-07-16, so other implementors could check.

## Reproduction Steps

To construct the announcement message, 'talkd' looks up a client's
domain name using its IP address and gethostbyaddr, which is typically a
reverse DNS lookup. The issue occurs when it returns a domain name
longer than around 200 characters. Here is the reproduction I shared
privately on the distros list. Note that the steps vary depending on
operating system, e.g., how 'inetd' starts:

    $ echo 'ntalk	dgram	udp	wait	tty:tty	/bin/talkd talkd' >> /etc/inetd.conf
    $ service inetd start
    $ printf '%s %s\n' <CLIENT-IP> $(yes | head -n 250 | tr -d '\n') >> /etc/hosts

Then, on the machine with <CLIENT-IP> run the following, where <HOST-IP>
is the machine running talkd and <USER> is a user you are logged into:

    $ talk <USER>@<CLIENT-IP>

If 'talkd' is affected, that should be enough to reproduce the
issue. Using a FreeBSD machine with an unpatched GNU Inetutils 2.8 build
with sanitizers disabled here is what I see:

    $ cat /var/log/messages
    [...]
    Jul 16 22:10:55 freebsd inetd[15837]: /home/collin/inetutils/talkd/talkd[16090]: exited, signal 10
    Jul 16 22:10:55 freebsd inetd[16091]: warning: /etc/hosts.allow, line 22: can't verify hostname: getaddrinfo(yyy..., AF_INET) failed
    Jul 16 22:10:55 freebsd inetd[15837]: /home/collin/inetutils/talkd/talkd[16091]: exited, signal 10
    Jul 16 22:10:55 freebsd inetd[16092]: warning: /etc/hosts.allow, line 22: can't verify hostname: getaddrinfo(yyy..., AF_INET) failed
    Jul 16 22:10:55 freebsd inetd[15837]: /home/collin/inetutils/talkd/talkd[16092]: exited, signal 10
    Jul 16 22:10:55 freebsd inetd[16093]: warning: /etc/hosts.allow, line 22: can't verify hostname: getaddrinfo(yyy..., AF_INET) failed
    Jul 16 22:10:55 freebsd inetd[15837]: /home/collin/inetutils/talkd/talkd[16093]: exited, signal 10
    Jul 16 22:10:55 freebsd inetd[15837]: ntalk/udp server failing (looping), service terminated
    Jul 16 22:10:55 freebsd kernel: pid 16023 (talkd), jid 0, uid 4: exited on signal 10 (no core dump - other error)
    Jul 16 22:10:55 freebsd kernel: pid 16024 (talkd), jid 0, uid 4: exited on signal 10 (no core dump - other error)
    Jul 16 22:10:55 freebsd kernel: pid 16025 (talkd), jid 0, uid 4: exited on signal 10 (no core dump - other error)
    Jul 16 22:10:55 freebsd kernel: pid 16026 (talkd), jid 0, uid 4: exited on signal 10 (no core dump - other error)
    Jul 16 22:10:55 freebsd kernel: pid 16027 (talkd), jid 0, uid 4: exited on signal 10 (no core dump - other error)

I.e., 'talkd' crashes with SIGBUS.

## Workarounds

Don't use 'talkd' in 2026. There are much better programs for users to
communicate nowadays. Interestingly, Debian removed the 'mesg' program
from their util-linux package for this reason [2].

Using ACLs, the -a or --acl option, would also protect you [3].
Although, I am curious how often that option actually gets used...

## Detailed Description

Here is a more detailed explanation of the issue, for reference. When
GNU 'talkd' processes a request, it first verifies it against the
configured ACLs. Triggering the issue requires the request not to be
denied by the ACL [4]:

  if (acl_match (msg, sa_in) == ACL_DENY)
    {
      /* Code unnecessary for describing the issue removed...  */
      return 0;
    }

The ACL mechanism [3] is not enabled by default, and likely very few
people use it. Well, very few people use 'talkd' at all, for that
matter.

If the message is an ANNOUNCE request, an announcement is sent to the
terminal of the user that they are trying to contact [5]:

    switch (msg->type)
      {
      case ANNOUNCE:
        do_announce (msg, rp);
      /* ...  */
      }

The announcement is not sent if 'mesg n' is in use:

    $ mesg n; mesg; stat --format=%A `tty`
    is n
    crw-------

That is opposed to 'mesg y', which allows the issue to be triggered:

    $ mesg y; mesg; stat --format=%A `tty`
    is y
    crw--w----

The relevant source code can be seen here [6]:

    sprintf (ttypath, "%s/%s", PATH_TTY_PFX, request->r_tty);
    rc = stat (ttypath, &st);
    free (ttypath);
    if (rc < 0 || (st.st_mode & S_IWGRP) == 0)
      return PERMISSION_DENIED;
    return print_mesg (request->r_tty, request, remote_machine);

This announcement contains the name of the host, as returned by
gethostbyaddr(3) [7]:

    hp = gethostbyaddr ((char *) &os2sin_addr (mp->ctl_addr),
                        sizeof (struct in_addr), AF_INET);

Here is the format of that announcement message [8], where
"your_machine" is hp->h_name, resulting from the above gethostbyaddr
call:

    Message from TalkDaemon@..._machine...
    talk: connection requested by your_name@...r_machine.
    talk: respond with: talk your_name@...r_machine

Each line ends in spaces up to the maximum line length plus two. The
message also starts with an ASCII BEL, CR, LF, followed by a line full
of spaces, and ends with a line full of spaces.

The issue is that the maximum line length is calculated using vsnprintf,
which returns the number of bytes required to store the resulting string
regardless of how many were actually written to the buffer [9]:

    static void
    format_line (LINE *lp, const char *fmt, ...)
    {
      /* Here lp->line[i] contains the line in the announcement string, and
         lp->size[i] contains the length of the line.  */
      lp->size[i] = vsnprintf (lp->line[i], sizeof lp->line[i], fmt, ap);
      /* We later pad each line "i" with spaces from
         lp->size[i] to lp->max_size + 2.  */
      lp->max_size = MAX (lp->max_size, lp->size[i]);
      /* ...  */
    }

These lines are then copied over to a 1283 (5 * 256 + 3) byte buffer
[10] [11]:

    #define N_LINES 5
    #define N_CHARS 256

    typedef struct
    {
      int ind;
      int max_size;
      char line[N_LINES][N_CHARS];
      int size[N_LINES];
      char buf[N_LINES * N_CHARS + 3];
    } LINE;

    /* ...  */

    static char *
    finish_line (LINE *lp)
    {
      int i;
      char *p;

      p = lp->buf;
      *p++ = '\a';
      *p++ = '\r';
      *p++ = '\n';
      for (i = 0; i < lp->ind; i++)
        {
          char *q;
          int j;

          for (q = lp->line[i]; *q; q++)
            *p++ = *q;
          for (j = lp->size[i]; j < lp->max_size + 2; j++)
            *p++ = ' ';
          *p++ = '\r';
          *p++ = '\n';
        }
      *p = 0;
      return lp->buf;
    }

In the above section of code, lp->size[2], lp->size[3], and lp->max_size
can be made large values by a remote user, such that data is written
past lp->buf, if their domain name exceeds ~200 bytes. Here lp->size[2]
and lp->size[3] refer to the following lines and lp->size[2] is equal to
lp->max_size:

    talk: connection requested by your_name@...g_name
    talk: respond with: talk your_name@...g_name

The original reporter said they were able to write approximately 138
bytes past the end of lp->buf. I did not test that myself, but since I
could reproduce the issue with a ~200-byte hostname added to my
/etc/hosts file, and domain names can be ~50 characters longer, that
number sounds about correct to me.

## Patch

The fix for this issue was pushed to the GNU Inetutils repository today.
Here is the associated commit:

https://codeberg.org/inetutils/inetutils/commit/81987d968ab831c08bd7e42a46f4a4823729cf1e

The patch applies cleanly to the latest release, inetutils-2.8. It can
be applied like so:

    $ curl --silent https://codeberg.org/inetutils/inetutils/commit/81987d968ab831c08bd7e42a46f4a4823729cf1e.patch | sed '1,41d' > talkd-fix.patch
    $ for a in sha2 sha3; do cksum -a $a -l 256 talkd-fix.patch; done
    SHA256 (talkd-fix.patch) = 5289d195b427020738953828c5851bf988be533843e015b50d95a49a7c34062c
    SHA3-256 (talkd-fix.patch) = 5ff65824225cb585e5ab6501f4a0550ade1ee8f75851b399b5c899ff41d5cf62
    $ patch -p1 < talkd-fix.patch
    patching file talkd/announce.c
    patching file talkd/intalkd.h
    patching file talkd/talkd.c

## Credits

Many thanks to Tristan Madani <tristan@...encesecurity.com> for the
detailed report and for reviewing the patch.

## Timeline

    2026-07-02: Report sent to inetutils-security@....org
    2026-07-02: I (Collin Funk) acknowledged the report and asked a few
                questions regarding the issue.
    2026-07-04: Tristan answered those questions.
    2026-07-06: I reproduced the issue updated Tristan with a planned
                timeline for the fix and CVE assignment.
    2026-07-08: Tristan agreed to the timeline and offered to review the
                patch.
    2026-07-11: I wrote the patch and sent it to Tristan.
    2026-07-15: Tristan confirmed the patch worked as expected.
    2026-07-16: Private mail to distros mailing list along with the patch.
    2026-07-24: I wrote this report and sent it to oss-security.

Note that I also requested a CVE when emailing distros, but haven't
heard back. I'll probably reach out privately to a CNA in a bit, and
will update here once one is assigned.

Collin

[1] https://codeberg.org/inetutils/inetutils/commit/9974e41e9fd957b0271d4dc0af773a9f998f1eb2
[2] https://sources.debian.org/src/util-linux/2.41-5/debian/NEWS#L1-L9
[3] https://www.gnu.org/software/inetutils/manual/inetutils.html#Access-control-in-talkd
[4] https://codeberg.org/inetutils/inetutils/src/commit/c4b87ff6cd5ebcd5e0b1de748eba348da38a0a15/talkd/process.c#L69-L84
[5] https://codeberg.org/inetutils/inetutils/src/commit/c4b87ff6cd5ebcd5e0b1de748eba348da38a0a15/talkd/process.c#L93-L96
[6] https://codeberg.org/inetutils/inetutils/src/commit/c4b87ff6cd5ebcd5e0b1de748eba348da38a0a15/talkd/announce.c#L140-L145
[7] https://codeberg.org/inetutils/inetutils/src/commit/c4b87ff6cd5ebcd5e0b1de748eba348da38a0a15/talkd/process.c#L162-L163
[8] https://www.gnu.org/software/inetutils/manual/inetutils.html#talk_003a-a-communication-program
[9] https://codeberg.org/inetutils/inetutils/src/commit/c4b87ff6cd5ebcd5e0b1de748eba348da38a0a15/talkd/announce.c#L47-L60
[10] https://codeberg.org/inetutils/inetutils/src/commit/c4b87ff6cd5ebcd5e0b1de748eba348da38a0a15/talkd/announce.c#L25-L39
[11] https://codeberg.org/inetutils/inetutils/src/commit/c4b87ff6cd5ebcd5e0b1de748eba348da38a0a15/talkd/announce.c#L62-L86

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.