CVE-2020-5209 (published 2020-01-28T18:15Z)

In NetHack before 3.6.5, unknown options starting with -de and -i can cause a buffer overflow resulting in a crash or remote code execution/privilege escalation. This vulnerability affects systems that have NetHack installed suid/sgid and shared systems that allow users to influence command line options. Users should upgrade to NetHack 3.6.5.

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

Weakness information:
cwe_id: CWE-120
cwe_name: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
description: The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
extended_description: A buffer overflow condition exists when a product attempts to put more data in a buffer than it can hold, or when it attempts to put data in a memory area outside of the boundaries of a buffer. The simplest type of error, and the most common cause of buffer overflows, is the "classic" case in which the product copies the buffer without restricting how much is copied. Other variants exist, but the existence of a classic overflow strongly suggests that the programmer is not considering even the most basic of security protections.
         

Fix information:
repo_url: https://github.com/NetHack/NetHack
commit_details: repo_url: https://github.com/NetHack/NetHack
author: PatR
author_date: 2020-01-16 05:22:18-08:00
msg: command line triggered buffer overruns

Prevent extremely long command line arguments from overflowing local
buffers in raw_printf or config_error_add.  The increased buffer
sizes they recently got to deal with long configuration file values
aren't sufficient to handle command line induced overflows.

choose_windows(core): copy and truncate the window_type argument in
case it gets passed to config_error_add().

process_options(unix): report bad values with "%.60s" so that vsprintf
will implicitly truncate when formatted by raw_printf().
repository: repo_url: https://github.com/NetHack/NetHack
repo_name: NetHack/NetHack
description: Official NetHack Git Repository
repo_language: C
owner: NetHack
file_changes: filename: fixes36.5
change_type: ModificationType.MODIFY
diff: @@ -13,6 +13,8 @@ ensure existing callers of string_for_opt() check return value before using it
 fix potential buffer overflow in add_menu_coloring()
 fix potential buffer overflow in sym_val()
 fix potential buffer overflow in pline(), raw_printf(), and config_error_add()
+	via bad config file values or command line arguments
+fix potential buffer overflow in choose_windows()
 
 
 Fixes to Post-3.6.4 Problems that Were Exposed Via git Repository

programming_language: Markdown
filename: topten.c
change_type: ModificationType.MODIFY
diff: @@ -1000,6 +1000,7 @@ int uid;
  * print selected parts of score list.
  * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
  * and argv[1] starting with "-s".
+ * caveat: some shells might allow argv elements to be arbitrarily long.
  */
 void
 prscore(argc, argv)

programming_language: C
filename: windows.c
change_type: ModificationType.MODIFY
diff: @@ -243,7 +243,8 @@ void
 choose_windows(s)
 const char *s;
 {
-    register int i;
+    int i;
+    char *tmps = 0;
 
     for (i = 0; winchoices[i].procs; i++) {
         if ('+' == winchoices[i].procs->name[0])
@@ -269,9 +270,22 @@ const char *s;
         windowprocs.win_wait_synch = def_wait_synch;
 
     if (!winchoices[0].procs) {
-        raw_printf("No window types?");
+        raw_printf("No window types supported?");
         nh_terminate(EXIT_FAILURE);
     }
+    /* 50: arbitrary, no real window_type names are anywhere near that long;
+       used to prevent potential raw_printf() overflow if user supplies a
+       very long string (on the order of 1200 chars) on the command line
+       (config file options can't get that big; they're truncated at 1023) */
+#define WINDOW_TYPE_MAXLEN 50
+    if (strlen(s) >= WINDOW_TYPE_MAXLEN) {
+        tmps = (char *) alloc(WINDOW_TYPE_MAXLEN);
+        (void) strncpy(tmps, s, WINDOW_TYPE_MAXLEN - 1);
+        tmps[WINDOW_TYPE_MAXLEN - 1] = '\0';
+        s = tmps;
+    }
+#undef WINDOW_TYPE_MAXLEN
+
     if (!winchoices[1].procs) {
         config_error_add(
                      "Window type %s not recognized.  The only choice is: %s",
@@ -293,6 +307,8 @@ const char *s;
         config_error_add("Window type %s not recognized.  Choices are:  %s",
                          s, buf);
     }
+    if (tmps)
+        free((genericptr_t) tmps) /*, tmps = 0*/ ;
 
     if (windowprocs.win_raw_print == def_raw_print
             || WINDOWPORT("safe-startup"))

programming_language: C
filename: unixmain.c
change_type: ModificationType.MODIFY
diff: @@ -355,6 +355,7 @@ char *argv[];
     return 0;
 }
 
+/* caveat: argv elements might be arbitrary long */
 static void
 process_options(argc, argv)
 int argc;
@@ -383,11 +384,10 @@ char *argv[];
                 load_symset("DECGraphics", PRIMARY);
                 switch_symbols(TRUE);
             } else {
-                raw_printf("Unknown option: %s", *argv);
+                raw_printf("Unknown option: %.60s", *argv);
             }
             break;
         case 'X':
-
             discover = TRUE, wizard = FALSE;
             break;
 #ifdef NEWS
@@ -413,7 +413,7 @@ char *argv[];
                 load_symset("RogueIBM", ROGUESET);
                 switch_symbols(TRUE);
             } else {
-                raw_printf("Unknown option: %s", *argv);
+                raw_printf("Unknown option: %.60s", *argv);
             }
             break;
         case 'p': /* profession (role) */
@@ -451,7 +451,7 @@ char *argv[];
                 flags.initrole = i;
                 break;
             }
-            /* else raw_printf("Unknown option: %s", *argv); */
+            /* else raw_printf("Unknown option: %.60s", *argv); */
         }
     }
 

programming_language: C