VYPR
Unrated severityNVD Advisory· Published Jul 17, 2018· Updated Aug 5, 2024

CVE-2018-14363

CVE-2018-14363

Description

An issue was discovered in NeoMutt before 2018-07-16. newsrc.c does not properly restrict '/' characters that may have unsafe interaction with cache pathnames.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

NeoMutt before 2018-07-16 allows cache path traversal via '/' in POP message IDs due to improper sanitization in newsrc.c.

Vulnerability

The vulnerability exists in NeoMutt's POP3 message caching mechanism. Before the 2018-07-16 release, the newsrc.c file did not sanitize POP message IDs that contain '/' characters. When these IDs were used to construct cache file paths, the '/' could lead to path traversal or unsafe file operations. Affected versions: NeoMutt before 2018-07-16 [2].

Exploitation

An attacker capable of controlling a POP message ID (e.g., by sending a malicious email with a crafted Message-ID header) can inject '/' characters. When NeoMutt fetches the message and stores it in the body cache, the unsanitized ID is used in file path operations, potentially allowing directory traversal.

Impact

Successful exploitation could allow an attacker to write cache files to arbitrary locations on the filesystem, potentially leading to arbitrary file write or other file system manipulation. The exact extent depends on file permissions and NeoMutt's configuration.

Mitigation

The fix was released in NeoMutt 2018-07-16 [2]. Users should upgrade to this version or later. The commit 9bfab35 [1] introduces the sanitization function cache_id() that strips or replaces problematic characters. There is no known workaround.

AI Insight generated on May 26, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

7

Patches

2
6a147a62cf39

merge: NeoMutt 2018-07-16

https://github.com/neomutt/neomuttRichard RussonJul 16, 2018via osv
41 files changed · +20281 20118
  • auto.def+1 1 modified
    @@ -14,7 +14,7 @@ use system cc cc-lib mutt-gettext mutt-iconv
     ###############################################################################
     # Names and versions
     define PACKAGE          "neomutt"
    -define PACKAGE_VERSION  "20180622"
    +define PACKAGE_VERSION  "20180716"
     define BUGS_ADDRESS     "neomutt-devel@neomutt.org"
     
     # Subdirectories that contain additional Makefile.autosetup files
    
  • ChangeLog.md+6 0 modified
    @@ -1,3 +1,9 @@
    +2018-07-16  Richard Russon  <rich@flatcap.org>
    +* Features
    +  - <check-stats> function
    +* Bug Fixes
    +  - Lots
    +
     2018-06-22  Richard Russon  <rich@flatcap.org>
     * Features
       - Expand variables inside backticks
    
  • doxygen/doxygen.conf+1 1 modified
    @@ -25,7 +25,7 @@ PROJECT_NAME           = "NeoMutt"
     # could be handy for archiving the generated documentation or if some version
     # control system is used.
     
    -PROJECT_NUMBER         = 2018-06-22
    +PROJECT_NUMBER         = 2018-07-16
     
     # Using the PROJECT_BRIEF tag one can provide an optional one line description
     # for a project that appears at the top of each page and should give viewer a
    
  • imap/auth_plain.c+2 1 modified
    @@ -77,7 +77,8 @@ enum ImapAuthRes imap_auth_plain(struct ImapData *idata, const char *method)
         }
         if (rc == IMAP_CMD_RESPOND)
         {
    -      mutt_str_strcat(buf + sizeof(auth_plain_cmd), sizeof(buf) - sizeof(auth_plain_cmd), "\r\n");
    +      mutt_str_strcat(buf + sizeof(auth_plain_cmd),
    +                      sizeof(buf) - sizeof(auth_plain_cmd), "\r\n");
           mutt_socket_send(idata->conn, buf + sizeof(auth_plain_cmd));
         }
       }
    
  • imap/imap.c+2 2 modified
    @@ -1730,8 +1730,8 @@ int imap_subscribe(char *path, bool subscribe)
         mutt_buffer_init(&err);
         err.data = errstr;
         err.dsize = sizeof(errstr);
    -	len = snprintf(mbox, sizeof(mbox), "%smailboxes ", subscribe ? "" : "un");
    -	imap_quote_string(mbox + len, sizeof(mbox) - len, path, true);
    +    len = snprintf(mbox, sizeof(mbox), "%smailboxes ", subscribe ? "" : "un");
    +    imap_quote_string(mbox + len, sizeof(mbox) - len, path, true);
         if (mutt_parse_rc_line(mbox, &token, &err))
           mutt_debug(1, "Error adding subscribed mailbox: %s\n", errstr);
         FREE(&token.data);
    
  • .mailmap+4 1 modified
    @@ -61,6 +61,7 @@ Jakub Jindra <jakub.jindra@socialbakers.com>                    Jakub Jindra <j
     Jakub Wilk <jwilk@jwilk.net>                                    Jakub Wilk <jwilk@jwilk.net>                             # @jwilk
     Jelle van der Waa <jelle@vdwaa.nl>                              Jelle van der Waa <jelle@vdwaa.nl>                       # @jelly
     Jenya Sovetkin <e.sovetkin@gmail.com>                           Jenya Sovetkin <e.sovetkin@gmail.com>                    # @esovetkin
    +JerikoOne <jeriko.one@gmx.us>                                   JerikoOne <jeriko.one@gmx.us>                            # @jeriko-one
     Joey Pabalinas <joeypabalinas@gmail.com>                        Joey Pabalinas <joeypabalinas@gmail.com>                 # @alyptik
     Johannes Weißl <jargon@molb.org>                               Johannes Weißl <jargon@molb.org>                        # @weisslj
     Jonathan Perkin <jperkin@netbsd.org>                            Jonathan Perkin <jperkin@netbsd.org>                     # @jperkin
    @@ -80,6 +81,7 @@ Marcin Rajner <mrajner@gik.pw.edu.pl>                           Marcin Rajner <m
     Marcin Rajner <mrajner@gik.pw.edu.pl>                           Marcin Rajner <mrajner@lenovo>                           # @mrajner
     Marcin Rajner <mrajner@gik.pw.edu.pl>                           Marcin Rajner lenovo <mrajner@gik.pw.edu.pl>             # @mrajner
     Marco Hinz <mh.codebro@gmail.com>                               Marco Hinz <mh.codebro@gmail.com>                        # @mhinz
    +Marco Sirabella <marco@sirabella.org>                           Marco Sirabella <marco@sirabella.org>                    # @mjsir911
     Marius Gedminas <marius@gedmin.as>                              Marius Gedminas <marius@gedmin.as>                       # @mgedmin
     Mehdi Abaakouk <sileht@sileht.net>                              Mehdi ABAAKOUK <sileht@sileht.net>                       # @sileht
     Mehdi Abaakouk <sileht@sileht.net>                              Mehdi Abaakouk <sileht@sileht.net>                       # @sileht
    @@ -156,7 +158,8 @@ Andreas Jobs <unknown>                                          Andreas Jobs <un
     Andrew Gaul <andrew@gaul.org>                                   Andrew Gaul <andrew@gaul.org>
     Andrew Nosenko <awn@bcs.zp.ua>                                  Andrew W. Nosenko <awn@bcs.zp.ua>
     Antoine Reilles <tonio@netbsd.org>                              Antoine Reilles <tonio@netbsd.org>
    -Anton Lindqvist <anton.lindqvist@gmail.com>                     Anton Lindqvist <anton.lindqvist@gmail.com>
    +Anton Lindqvist <anton@basename.se>                             Anton Lindqvist <anton.lindqvist@gmail.com>
    +Anton Lindqvist <anton@basename.se>                             Anton Lindqvist <anton@basename.se>
     Armin Wolfermann <aw@osn.de>                                    Armin Wolfermann <aw@osn.de>
     Aron Griffis <agriffis@n01se.net>                               Aron Griffis <agriffis@n01se.net>
     Athanasios Douitsis <aduitsis@gmail.com>                        Athanasios Douitsis <aduitsis@gmail.com>
    
  • newsrc.c+2 1 modified
    @@ -601,7 +601,8 @@ int nntp_add_group(char *line, void *data)
         return 0;
     
       /* These sscanf limits must match the sizes of the group and desc arrays */
    -  if (sscanf(line, "%1023s " ANUM " " ANUM " %c %8191[^\n]", group, &last, &first, &mod, desc) < 4)
    +  if (sscanf(line, "%1023s " ANUM " " ANUM " %c %8191[^\n]", group, &last,
    +             &first, &mod, desc) < 4)
       {
         mutt_debug(4, "Cannot parse server line: %s\n", line);
         return 0;
    
  • nntp.c+1 1 modified
    @@ -1289,7 +1289,7 @@ static int nntp_fetch_headers(struct Context *ctx, void *hc, anum_t first,
       fc.restore = restore;
       fc.messages = mutt_mem_calloc(last - first + 1, sizeof(unsigned char));
       if (fc.messages == NULL)
    -	  return -1;
    +    return -1;
     #ifdef USE_HCACHE
       fc.hc = hc;
     #endif
    
  • pattern.c+8 8 modified
    @@ -1885,13 +1885,13 @@ int mutt_pattern_exec(struct Pattern *pat, enum PatternExecFlag flags,
         case MUTT_SENDER:
           if (!h->env)
             return 0;
    -      return (pat->not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 1,
    -                                        h->env->sender));
    +      return (pat->not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS),
    +                                        1, h->env->sender));
         case MUTT_FROM:
           if (!h->env)
             return 0;
    -      return (pat->not ^
    -              match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 1, h->env->from));
    +      return (pat->not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS),
    +                                        1, h->env->from));
         case MUTT_TO:
           if (!h->env)
             return 0;
    @@ -1924,14 +1924,14 @@ int mutt_pattern_exec(struct Pattern *pat, enum PatternExecFlag flags,
         case MUTT_ADDRESS:
           if (!h->env)
             return 0;
    -      return (pat->not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 4,
    -                                        h->env->from, h->env->sender,
    +      return (pat->not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS),
    +                                        4, h->env->from, h->env->sender,
                                             h->env->to, h->env->cc));
         case MUTT_RECIPIENT:
           if (!h->env)
             return 0;
    -      return (pat->not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 2,
    -                                        h->env->to, h->env->cc));
    +      return (pat->not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS),
    +                                        2, h->env->to, h->env->cc));
         case MUTT_LIST: /* known list, subscribed or not */
           if (!h->env)
             return 0;
    
  • po/bg.po+675 670 modified
  • po/ca.po+675 670 modified
  • po/cs.po+675 670 modified
  • po/da.po+675 670 modified
  • po/de.po+675 670 modified
  • po/el.po+675 670 modified
  • po/en_GB.po+674 670 modified
  • po/eo.po+675 670 modified
  • po/es.po+675 670 modified
  • po/et.po+675 670 modified
  • po/eu.po+675 670 modified
  • po/fr.po+675 670 modified
  • po/ga.po+675 670 modified
  • po/gl.po+675 670 modified
  • po/hu.po+675 670 modified
  • po/id.po+675 670 modified
  • po/it.po+675 670 modified
  • po/ja.po+675 670 modified
  • po/ko.po+675 670 modified
  • po/lt.po+675 670 modified
  • po/nl.po+675 670 modified
  • po/pl.po+675 670 modified
  • po/pt_BR.po+675 670 modified
  • po/ru.po+675 670 modified
  • po/sk.po+675 670 modified
  • po/sv.po+675 670 modified
  • po/tr.po+675 670 modified
  • po/uk.po+675 670 modified
  • po/zh_CN.po+675 670 modified
  • po/zh_TW.po+675 670 modified
  • README.md+3 1 modified
    @@ -2,7 +2,7 @@
     
     [![Stars](https://img.shields.io/github/stars/neomutt/neomutt.svg?style=social&label=Stars)](https://github.com/neomutt/neomutt "Give us a Star")
     [![Twitter](https://img.shields.io/twitter/follow/NeoMutt_Org.svg?style=social&label=Follow)](https://twitter.com/NeoMutt_Org "Follow us on Twitter")
    -[![Contributors](https://img.shields.io/badge/Contributors-127-orange.svg)](#contributors "All of NeoMutt's Contributors")
    +[![Contributors](https://img.shields.io/badge/Contributors-132-orange.svg)](#contributors "All of NeoMutt's Contributors")
     [![Release](https://img.shields.io/github/release/neomutt/neomutt.svg)](https://github.com/neomutt/neomutt/releases/latest "Latest Release Notes")
     [![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-blue.svg)](https://github.com/neomutt/neomutt/blob/master/COPYRIGHT.md "Copyright Statement")
     [![Code build](https://img.shields.io/travis/neomutt/neomutt.svg?label=code)](https://travis-ci.org/neomutt/neomutt "Latest Automatic Code Build")
    @@ -137,6 +137,7 @@ Here's a list of everyone who's helped NeoMutt:
     [Jasper Adriaanse](https://github.com/jasperla "jasperla"),
     [Jelle van der Waa](https://github.com/jelly "jelly"),
     [Jenya Sovetkin](https://github.com/esovetkin "esovetkin"),
    +[JerikoOne](https://github.com/jeriko-one "jeriko-one"),
     [Joey Pabalinas](https://github.com/alyptik "alyptik"),
     [Johannes Frankenau](https://github.com/tsuflux "tsuflux"),
     [Johannes Weißl](https://github.com/weisslj "weisslj"),
    @@ -156,6 +157,7 @@ Here's a list of everyone who's helped NeoMutt:
     [Manos Pitsidianakis](https://github.com/epilys "epilys"),
     [Marcin Rajner](https://github.com/mrajner "mrajner"),
     [Marco Hinz](https://github.com/mhinz "mhinz"),
    +[Marco Sirabella](https://github.com/mjsir911 "mjsir911"),
     [Marius Gedminas](https://github.com/mgedmin "mgedmin"),
     [Mateusz Piotrowski](https://github.com/0mp "0mp"),
     [Matteo Vescovi](https://github.com/mfvescovi "mfvescovi"),
    
  • send.c+2 1 modified
    @@ -1076,7 +1076,8 @@ struct Address *mutt_default_from(void)
     
       if (From)
         addr = mutt_addr_copy(From);
    -  else {
    +  else
    +  {
         addr = mutt_addr_new();
         if (UseDomain)
         {
    
9bfab3552230

sanitise cache paths

https://github.com/neomutt/neomuttRichard RussonJul 5, 2018via osv
2 files changed · +35 7
  • newsrc.c+12 1 modified
    @@ -715,7 +715,18 @@ int nntp_active_save_cache(struct NntpServer *nserv)
      */
     static int nntp_hcache_namer(const char *path, char *dest, size_t destlen)
     {
    -  return snprintf(dest, destlen, "%s.hcache", path);
    +  int count = snprintf(dest, destlen, "%s.hcache", path);
    +
    +  /* Strip out any directories in the path */
    +  char *first = strchr(dest, '/');
    +  char *last = strrchr(dest, '/');
    +  if (first && last && (last > first))
    +  {
    +    memmove(first, last, strlen(last) + 1);
    +    count -= (last - first);
    +  }
    +
    +  return count;
     }
     
     /**
    
  • pop.c+23 6 modified
    @@ -63,6 +63,23 @@
     #define HC_FEXT "hcache"   /* extension for hcache as POP lacks paths */
     #endif
     
    +/**
    + * cache_id - Make a message-cache-compatible id
    + * @param id POP message id
    + * @retval ptr Sanitised string
    + *
    + * The POP message id may contain '/' and other awkward characters.
    + *
    + * @note This function returns a pointer to a static buffer.
    + */
    +static const char *cache_id(const char *id)
    +{
    +  static char clean[SHORT_STRING];
    +  mutt_str_strfcpy(clean, id, sizeof(clean));
    +  mutt_file_sanitize_filename(clean, true);
    +  return clean;
    +}
    +
     /**
      * fetch_message - write line to file
      * @param line String to write
    @@ -242,7 +259,7 @@ static int msg_cache_check(const char *id, struct BodyCache *bcache, void *data)
       /* message not found in context -> remove it from cache
        * return the result of bcache, so we stop upon its first error
        */
    -  return mutt_bcache_del(bcache, id);
    +  return mutt_bcache_del(bcache, cache_id(id));
     }
     
     #ifdef USE_HCACHE
    @@ -407,7 +424,7 @@ static int pop_fetch_headers(struct Context *ctx)
            *        - if we don't have a body: new
            */
           const bool bcached =
    -          (mutt_bcache_exists(pop_data->bcache, ctx->hdrs[i]->data) == 0);
    +          (mutt_bcache_exists(pop_data->bcache, cache_id(ctx->hdrs[i]->data)) == 0);
           ctx->hdrs[i]->old = false;
           ctx->hdrs[i]->read = false;
           if (hcached)
    @@ -597,7 +614,7 @@ static int pop_fetch_message(struct Context *ctx, struct Message *msg, int msgno
       unsigned short bcache = 1;
     
       /* see if we already have the message in body cache */
    -  msg->fp = mutt_bcache_get(pop_data->bcache, h->data);
    +  msg->fp = mutt_bcache_get(pop_data->bcache, cache_id(h->data));
       if (msg->fp)
         return 0;
     
    @@ -644,7 +661,7 @@ static int pop_fetch_message(struct Context *ctx, struct Message *msg, int msgno
                            NetInc, h->content->length + h->content->offset - 1);
     
         /* see if we can put in body cache; use our cache as fallback */
    -    msg->fp = mutt_bcache_put(pop_data->bcache, h->data);
    +    msg->fp = mutt_bcache_put(pop_data->bcache, cache_id(h->data));
         if (!msg->fp)
         {
           /* no */
    @@ -689,7 +706,7 @@ static int pop_fetch_message(struct Context *ctx, struct Message *msg, int msgno
        * portion of the headers, those required for the main display.
        */
       if (bcache)
    -    mutt_bcache_commit(pop_data->bcache, h->data);
    +    mutt_bcache_commit(pop_data->bcache, cache_id(h->data));
       else
       {
         cache->index = h->index;
    @@ -783,7 +800,7 @@ static int pop_sync_mailbox(struct Context *ctx, int *index_hint)
             ret = pop_query(pop_data, buf, sizeof(buf));
             if (ret == 0)
             {
    -          mutt_bcache_del(pop_data->bcache, ctx->hdrs[i]->data);
    +          mutt_bcache_del(pop_data->bcache, cache_id(ctx->hdrs[i]->data));
     #ifdef USE_HCACHE
               mutt_hcache_delete(hc, ctx->hdrs[i]->data, strlen(ctx->hdrs[i]->data));
     #endif
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.