CVE-2018-14357 (published 2018-07-17T17:29Z)

An issue was discovered in Mutt before 1.10.1 and NeoMutt before 2018-07-16. They allow remote IMAP servers to execute arbitrary commands via backquote characters, related to the mailboxes command associated with an automatic subscription.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-78
cwe_name: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
description: The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
extended_description: 
		   This weakness can lead to a vulnerability in environments in which the attacker does not have direct access to the operating system, such as in web applications. Alternately, if the weakness occurs in a privileged program, it could allow the attacker to specify commands that normally would not be accessible, or to call alternate commands with privileges that the attacker does not have. The problem is exacerbated if the compromised process does not follow the principle of least privilege, because the attacker-controlled commands may run with special system privileges that increases the amount of damage.
            There are at least two subtypes of OS command injection:
               
				 The application intends to execute a single, fixed program that is under its own control. It intends to use externally-supplied inputs as arguments to that program. For example, the program might use system("nslookup [HOSTNAME]") to run nslookup and allow the user to supply a HOSTNAME, which is used as an argument. Attackers cannot prevent nslookup from executing. However, if the program does not remove command separators from the HOSTNAME argument, attackers could place the separators into the arguments, which allows them to execute their own program after nslookup has finished executing.
				 The application accepts an input that it uses to fully select which program to run, as well as which commands to use. The application simply redirects this entire command to the operating system. For example, the program might use "exec([COMMAND])" to execute the [COMMAND] that was supplied by the user. If the COMMAND is under attacker control, then the attacker can execute arbitrary commands or programs. If the command is being executed using functions like exec() and CreateProcess(), the attacker might not be able to combine multiple commands together in the same line.
               
            From a weakness standpoint, these variants represent distinct programmer errors. In the first variant, the programmer clearly intends that input from untrusted parties will be part of the arguments in the command to be executed. In the second variant, the programmer does not intend for the command to be accessible to any untrusted party, but the programmer probably has not accounted for alternate ways in which malicious attackers can provide input.
         
         

Fix information:
repo_url: https://gitlab.com/muttmua/mutt
commit_details: repo_url: https://gitlab.com/muttmua/mutt
author: Kevin McCarthy
author_date: 2018-07-07 19:03:44-07:00
msg: Properly quote IMAP mailbox names when (un)subscribing.

When handling automatic subscription (via $imap_check_subscribed), or
manual subscribe/unsubscribe commands, mutt generating a "mailboxes"
command but failed to properly escape backquotes.

Thanks to Jeriko One for the detailed bug report and patch, which this
commit is based upon.
repository: repo_url: https://gitlab.com/muttmua/mutt
repo_name: visit repo url
description: visit repo url
repo_language: visit repo url
owner: muttmua
file_changes: filename: command.c
change_type: ModificationType.MODIFY
diff: @@ -842,8 +842,9 @@ static void cmd_parse_lsub (IMAP_DATA* idata, char* s)
 
   strfcpy (buf, "mailboxes \"", sizeof (buf));
   mutt_account_tourl (&idata->conn->account, &url);
-  /* escape \ and " */
-  imap_quote_string(errstr, sizeof (errstr), list.name);
+  /* escape \ and ". Also escape ` because the resulting
+   * string will be passed to mutt_parse_rc_line. */
+  imap_quote_string_and_backquotes (errstr, sizeof (errstr), list.name);
   url.path = errstr + 1;
   url.path[strlen(url.path) - 1] = '\0';
   if (!mutt_strcmp (url.user, ImapUser))

programming_language: C
filename: imap.c
change_type: ModificationType.MODIFY
diff: @@ -1930,6 +1930,7 @@ int imap_subscribe (char *path, int subscribe)
   char buf[LONG_STRING];
   char mbox[LONG_STRING];
   char errstr[STRING];
+  int mblen;
   BUFFER err, token;
   IMAP_MBOX mx;
 
@@ -1951,8 +1952,10 @@ int imap_subscribe (char *path, int subscribe)
     mutt_buffer_init (&err);
     err.data = errstr;
     err.dsize = sizeof (errstr);
-    snprintf (mbox, sizeof (mbox), "%smailboxes \"%s\"",
-              subscribe ? "" : "un", path);
+    mblen = snprintf (mbox, sizeof (mbox), "%smailboxes ",
+                      subscribe ? "" : "un");
+    imap_quote_string_and_backquotes (mbox + mblen, sizeof(mbox) - mblen,
+                                      path);
     if (mutt_parse_rc_line (mbox, &token, &err))
       dprint (1, (debugfile, "Error adding subscribed mailbox: %s\n", errstr));
     FREE (&token.data);

programming_language: C
filename: imap_private.h
change_type: ModificationType.MODIFY
diff: @@ -301,7 +301,8 @@ char* imap_next_word (char* s);
 time_t imap_parse_date (char* s);
 void imap_make_date (char* buf, time_t timestamp);
 void imap_qualify_path (char *dest, size_t len, IMAP_MBOX *mx, char* path);
-void imap_quote_string (char* dest, size_t slen, const char* src);
+void imap_quote_string (char* dest, size_t dlen, const char* src);
+void imap_quote_string_and_backquotes (char *dest, size_t dlen, const char *src);
 void imap_unquote_string (char* s);
 void imap_munge_mbox_name (IMAP_DATA *idata, char *dest, size_t dlen, const char *src);
 void imap_unmunge_mbox_name (IMAP_DATA *idata, char *s);

programming_language: C
filename: util.c
change_type: ModificationType.MODIFY
diff: @@ -608,11 +608,9 @@ void imap_qualify_path (char *dest, size_t len, IMAP_MBOX *mx, char* path)
 }
 
 
-/* imap_quote_string: quote string according to IMAP rules:
- *   surround string with quotes, escape " and \ with \ */
-void imap_quote_string (char *dest, size_t dlen, const char *src)
+static void _imap_quote_string (char *dest, size_t dlen, const char *src,
+                                const char *to_quote)
 {
-  static const char quote[] = "\"\\";
   char *pt;
   const char *s;
 
@@ -625,7 +623,7 @@ void imap_quote_string (char *dest, size_t dlen, const char *src)
 
   for (; *s && dlen; s++)
   {
-    if (strchr (quote, *s))
+    if (strchr (to_quote, *s))
     {
       dlen -= 2;
       if (!dlen)
@@ -643,6 +641,23 @@ void imap_quote_string (char *dest, size_t dlen, const char *src)
   *pt = 0;
 }
 
+/* imap_quote_string: quote string according to IMAP rules:
+ *   surround string with quotes, escape " and \ with \ */
+void imap_quote_string (char *dest, size_t dlen, const char *src)
+{
+  _imap_quote_string (dest, dlen, src, "\"\\");
+}
+
+/* imap_quote_string_and_backquotes: quote string according to IMAP rules:
+ *   surround string with quotes, escape " and \ with \.
+ * Additionally, escape backquotes with \ to protect against code injection
+ * when using the resulting string in mutt_parse_rc_line().
+ */
+void imap_quote_string_and_backquotes (char *dest, size_t dlen, const char *src)
+{
+  _imap_quote_string (dest, dlen, src, "\"\\`");
+}
+
 /* imap_unquote_string: equally stupid unquoting routine */
 void imap_unquote_string (char *s)
 {

programming_language: C
repo_url: https://github.com/neomutt/neomutt
commit_details: repo_url: https://github.com/neomutt/neomutt
author: Richard Russon
author_date: 2018-07-06 00:52:41+01:00
msg: quote imap strings more carefully

Co-authored-by: JerikoOne <jeriko.one@gmx.us>
repository: repo_url: https://github.com/neomutt/neomutt
repo_name: neomutt/neomutt
description: ✉️ Teaching an Old Dog New Tricks -- IRC: #neomutt on irc.libera.chat
repo_language: C
owner: neomutt
file_changes: filename: auth_login.c
change_type: ModificationType.MODIFY
diff: @@ -65,8 +65,8 @@ enum ImapAuthRes imap_auth_login(struct ImapData *idata, const char *method)
 
   mutt_message(_("Logging in..."));
 
-  imap_quote_string(q_user, sizeof(q_user), idata->conn->account.user);
-  imap_quote_string(q_pass, sizeof(q_pass), idata->conn->account.pass);
+  imap_quote_string(q_user, sizeof(q_user), idata->conn->account.user, false);
+  imap_quote_string(q_pass, sizeof(q_pass), idata->conn->account.pass, false);
 
   /* don't print the password unless we're at the ungodly debugging level
    * of 5 or higher */

programming_language: C
filename: command.c
change_type: ModificationType.MODIFY
diff: @@ -499,7 +499,7 @@ static void cmd_parse_lsub(struct ImapData *idata, char *s)
   mutt_str_strfcpy(buf, "mailboxes \"", sizeof(buf));
   mutt_account_tourl(&idata->conn->account, &url);
   /* escape \ and " */
-  imap_quote_string(errstr, sizeof(errstr), list.name);
+  imap_quote_string(errstr, sizeof(errstr), list.name, true);
   url.path = errstr + 1;
   url.path[strlen(url.path) - 1] = '\0';
   if (mutt_str_strcmp(url.user, ImapUser) == 0)

programming_language: C
filename: imap.c
change_type: ModificationType.MODIFY
diff: @@ -464,7 +464,7 @@ static int compile_search(struct Context *ctx, const struct Pattern *pat, struct
           return -1;
         }
         *delim = '\0';
-        imap_quote_string(term, sizeof(term), pat->p.str);
+        imap_quote_string(term, sizeof(term), pat->p.str, false);
         mutt_buffer_addstr(buf, term);
         mutt_buffer_addch(buf, ' ');
 
@@ -472,17 +472,17 @@ static int compile_search(struct Context *ctx, const struct Pattern *pat, struct
         *delim = ':';
         delim++;
         SKIPWS(delim);
-        imap_quote_string(term, sizeof(term), delim);
+        imap_quote_string(term, sizeof(term), delim, false);
         mutt_buffer_addstr(buf, term);
         break;
       case MUTT_BODY:
         mutt_buffer_addstr(buf, "BODY ");
-        imap_quote_string(term, sizeof(term), pat->p.str);
+        imap_quote_string(term, sizeof(term), pat->p.str, false);
         mutt_buffer_addstr(buf, term);
         break;
       case MUTT_WHOLE_MSG:
         mutt_buffer_addstr(buf, "TEXT ");
-        imap_quote_string(term, sizeof(term), pat->p.str);
+        imap_quote_string(term, sizeof(term), pat->p.str, false);
         mutt_buffer_addstr(buf, term);
         break;
       case MUTT_SERVERSEARCH:
@@ -495,7 +495,7 @@ static int compile_search(struct Context *ctx, const struct Pattern *pat, struct
         }
       }
         mutt_buffer_addstr(buf, "X-GM-RAW ");
-        imap_quote_string(term, sizeof(term), pat->p.str);
+        imap_quote_string(term, sizeof(term), pat->p.str, false);
         mutt_buffer_addstr(buf, term);
         break;
     }

programming_language: C
filename: imap_private.h
change_type: ModificationType.MODIFY
diff: @@ -328,7 +328,7 @@ char *imap_get_qualifier(char *buf);
 int imap_mxcmp(const char *mx1, const char *mx2);
 char *imap_next_word(char *s);
 void imap_qualify_path(char *dest, size_t len, struct ImapMbox *mx, char *path);
-void imap_quote_string(char *dest, size_t dlen, const char *src);
+void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick);
 void imap_unquote_string(char *s);
 void imap_munge_mbox_name(struct ImapData *idata, char *dest, size_t dlen, const char *src);
 void imap_unmunge_mbox_name(struct ImapData *idata, char *s);

programming_language: C
filename: util.c
change_type: ModificationType.MODIFY
diff: @@ -798,9 +798,12 @@ void imap_qualify_path(char *dest, size_t len, struct ImapMbox *mx, char *path)
  *
  * Surround string with quotes, escape " and \ with backslash
  */
-void imap_quote_string(char *dest, size_t dlen, const char *src)
+void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
 {
-  static const char quote[] = "\"\\";
+  const char *quote = "`\"\\";
+  if (!quote_backtick)
+    quote++;
+
   char *pt = dest;
   const char *s = src;
 
@@ -874,7 +877,7 @@ void imap_munge_mbox_name(struct ImapData *idata, char *dest, size_t dlen, const
   char *buf = mutt_str_strdup(src);
   imap_utf_encode(idata, &buf);
 
-  imap_quote_string(dest, dlen, buf);
+  imap_quote_string(dest, dlen, buf, false);
 
   FREE(&buf);
 }

programming_language: C