[note: I am not subscribed to debian-security; please keep me or debian-lts addressed on replies]
Hello all, I have been working on trying to reproduce CVE-2018-19518 in uw-imap. I had already prepared PHP updates for jessie and wheezy to address that aspect of the vulnerability, though neither of those required an understanding of the underlying issue since the PHP team went the route of disabling rsh/ssh functionality in uw-imap. As it happens, alpine embeds the uw-imap code and that happened to serve as a useful testbed for reproducing the vulnerability, understading the underlying issue, and then developing/testing a fix. I would appreciate comments on my analysis and proposed patch. To start, I downloaded the source for alpine 2.20+dfsg1-7 in stretch. I then tried to reproduce CVE-2018-19518 using a variety of the approaches which were described on the web (especially the metasploit example from Exploit DB). However, as best I can tell all of the examples are focused on the PHP route and none of them worked when specified in a ~/.pinerc file. So, I simplified and settled on this simple configuration directive in ~/.pinerc: inbox-path={x -oProxyCommand=`date>ohai`}inbox After placing that directive in ~/.pinerc and launching alpine I ended up with a file ('ohai') in the current working directory with the current system date/time. That was enough, I thought, to consider that I had something which resembled the reported vulnerability. After I had that, I began to consider the nature of the problem more deeply. In particular, I wondered whether it was possible to answer the question, "is this is a valid host name?" I also wondered whether it was possible to cause the vulnerability without a space in the hostname (somewhat related to the first question). In any event, I concluded that the question of whether something is a valid hostname might be a bit complex to tackle and despite numerous attempts I was not able to exploit the vulnerability without the space between the host name and the command switch '-'. It did not seem sensible to consider attempting to detect the ProxyCommand options since that seems like it would leave the door open for other related vulnerabilities. For example, might there be an as yet unexplored opening with LocalCommand or ProxyJump? After digging into the code in uw-imap that parses the configuration file value into the rsh or ssh command line (the tcp_aopen function in tcp_unix.c), and further considering the necessity of the space to making the exploit work, I decided that checking to ensure that the parsed command line has the same number of tokens as the command template string would be enough to tell me that there was an attempt at a command injection. Based on that, I came up with the attached patch. If anyone wishes to try this out, all that is needed is to install the stretch version of alpine and create ~/.pinerc to contain the above directive. I have also built and uploaded a version that contains my candidate fix here: https://people.debian.org/~roberto/alpine_2.20+dfsg1-7~0.dsc If this seems like a sensible approach, I propose to apply the attached patch to uw-imap 8:2007f~dfsg-5 (the current stretch/buster/sid version) to create version 8:2007f~dfsg-6 for upload to sid and eventual inclusion in stretch (perhaps via a point release) and then also in parallel create a 8:2007f~dfsg-4+deb8u1 package for upload to jessie. Please reply with your comments. In particular, feedback from the security team on the appropriateness of this for a stable point release and my suggested route for the update to take to get there would be very useful. Regards, -Roberto -- Roberto C. Sánchez
--- alpine.git.orig/imap/src/osdep/unix/tcp_unix.c +++ alpine.git/imap/src/osdep/unix/tcp_unix.c @@ -341,8 +341,8 @@ { TCPSTREAM *stream = NIL; void *adr; - char host[MAILTMPLEN],tmp[MAILTMPLEN],*path,*argv[MAXARGV+1],*r; - int i,ti,pipei[2],pipeo[2]; + char host[MAILTMPLEN],tmp[MAILTMPLEN],*path,*argv[MAXARGV+1],*r,*fmt_arg; + int i,ti,pipei[2],pipeo[2],fmt_argc; size_t len; time_t now; struct timeval tmo; @@ -383,11 +383,21 @@ fs_give((void **) &r); } - if (*service == '*') /* build ssh command */ + if (*service == '*') { /* build ssh command */ sprintf (tmp,sshcommand,sshpath,host, mb->user[0] ? mb->user : myusername (),service + 1); - else sprintf (tmp,rshcommand,rshpath,host, - mb->user[0] ? mb->user : myusername (),service); + /* count tokens in command string template */ + for (i = 1,strtok_r (sshcommand," ",&fmt_arg); + (i < MAXARGV) && (strtok_r (NIL," ",&fmt_arg)); i++); + fmt_argc = i; + } else { + sprintf (tmp,rshcommand,rshpath,host, + mb->user[0] ? mb->user : myusername (),service); + /* count tokens in command string template */ + for (i = 1,strtok_r (rshcommand," ",&fmt_arg); + (i < MAXARGV) && (strtok_r (NIL," ",&fmt_arg)); i++); + fmt_argc = i; + } if (tcpdebug) { char msg[MAILTMPLEN]; sprintf (msg,"Trying %.100s",tmp); @@ -397,6 +407,12 @@ for (i = 1,path = argv[0] = strtok_r (tmp," ",&r); (i < MAXARGV) && (argv[i] = strtok_r (NIL," ",&r)); i++); argv[i] = NIL; /* make sure argv tied off */ + /* check for argument injection */ + if (fmt_argc != i) { + sprintf (tmp,"Command format token count mismatch: possible command injection attempt"); + mm_log (tmp,ERROR); + return NIL; + } /* make command pipes */ if (pipe (pipei) < 0) return NIL; if ((pipei[0] >= FD_SETSIZE) || (pipei[1] >= FD_SETSIZE) ||