CVE-2014-6262 (published 2020-02-12T02:15Z)

Multiple format string vulnerabilities in the python module in RRDtool, as used in Zenoss Core before 4.2.5 and other products, allow remote attackers to execute arbitrary code or cause a denial of service (application crash) via a crafted third argument to the rrdtool.graph function, aka ZEN-15415, a related issue to CVE-2013-2131.

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

Weakness information:
cwe_id: CWE-134
cwe_name: Use of Externally-Controlled Format String
description: The product uses a function that accepts a format string as an argument, but the format string originates from an external source.
extended_description: 
            When an attacker can modify an externally-controlled format string, this can lead to buffer overflows, denial of service, or data representation problems.
            It should be noted that in some circumstances, such as internationalization, the set of format strings is externally controlled by design. If the source of these format strings is trusted (e.g. only contained in library files that are only modifiable by the system administrator), then the external control might not itself pose a vulnerability.
         
         

Fix information:
repo_url: https://github.com/oetiker/rrdtool-1.x
commit_details: repo_url: https://github.com/oetiker/rrdtool-1.x
author: Tobias Oetiker
author_date: 2014-09-16 09:48:26+02:00
msg: a proper fix to the bad_format checking misery

Conflicts:

	src/rrd_graph.c
repository: repo_url: https://github.com/oetiker/rrdtool-1.x
repo_name: oetiker/rrdtool-1.x
description: RRDtool 1.x - Round Robin Database
repo_language: C
owner: oetiker
file_changes: filename: rrd_graph.c
change_type: ModificationType.MODIFY
diff: @@ -6,6 +6,7 @@
 
 
 #include <sys/stat.h>
+#include <glib.h>   // will use regex
 
 #ifdef WIN32
 #include "strftime.h"
@@ -1674,9 +1675,7 @@ int print_calc(
                         strftime(prline.u_str,
                                  FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
                     }
-                } else if (bad_format(im->gdes[i].format)) {
-                    rrd_set_error
-                        ("bad format for PRINT in '%s'", im->gdes[i].format);
+                } else if (bad_format_print(im->gdes[i].format)) {
                     return -1;
                 } else {
                     prline.u_str =
@@ -1697,11 +1696,8 @@ int print_calc(
                                  FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
                     }
                 } else {
-                    if (bad_format(im->gdes[i].format)) {
-                        rrd_set_error
-                            ("bad format for GPRINT in '%s'",
-                             im->gdes[i].format);
-                        return -1;
+                    if (bad_format_print(im->gdes[i].format)) {
+                       return -1;
                     }
                     snprintf(im->gdes[i].legend,
                              FMT_LEG_LEN - 2,
@@ -4172,7 +4168,6 @@ int rrd_graph(
     return 0;
 }
 
-
 /* Some surgery done on this function, it became ridiculously big.
 ** Things moved:
 ** - initializing     now in rrd_graph_init()
@@ -4243,7 +4238,6 @@ rrd_info_t *rrd_graph_v(
         if (bad_format_imginfo(im.imginfo)) {
             rrd_info_free(im.grinfo);
             im_free(&im);
-            rrd_set_error("bad format for imginfo");
             return NULL;
         }
         path = strdup(im.graphfile);
@@ -4717,8 +4711,7 @@ void rrd_graph_options(
             }
             break;
         case 1004:
-            if (bad_format(optarg)){
-                rrd_set_error("use either %le or %lf formats");
+            if (bad_format_axis(optarg)){
                 return;
             }
             im->second_axis_format=strdup(optarg);
@@ -4728,8 +4721,7 @@ void rrd_graph_options(
             }
             break;
         case 1012:
-            if (bad_format(optarg)){
-                rrd_set_error("use either %le or %lf formats");
+            if (bad_format_axis(optarg)){
                 return;
             }
             im->primary_axis_format=strdup(optarg);
@@ -5067,99 +5059,38 @@ int rrd_graph_color(
 }
 
 
-int bad_format(
-    char *fmt)
-{
-    char     *ptr;
-    int       n = 0;
-
-    ptr = fmt;
-    while (*ptr != '\0')
-        if (*ptr++ == '%') {
-
-            /* line cannot end with percent char */
-            if (*ptr == '\0')
-                return 1;
-            /* '%s', '%S' and '%%' are allowed */
-            if (*ptr == 's' || *ptr == 'S' || *ptr == '%')
-                ptr++;
-            /* %c is allowed (but use only with vdef!) */
-            else if (*ptr == 'c') {
-                ptr++;
-                n = 1;
-            }
-
-            /* or else '% 6.2lf' and such are allowed */
-            else {
-                /* optional padding character */
-                if (*ptr == ' ' || *ptr == '+' || *ptr == '-')
-                    ptr++;
-                /* This should take care of 'm.n' with all three optional */
-                while (*ptr >= '0' && *ptr <= '9')
-                    ptr++;
-                if (*ptr == '.')
-                    ptr++;
-                while (*ptr >= '0' && *ptr <= '9')
-                    ptr++;
-                /* Either 'le', 'lf' or 'lg' must follow here */
-                if (*ptr++ != 'l')
-                    return 1;
-                if (*ptr == 'e' || *ptr == 'f' || *ptr == 'g')
-                    ptr++;
-                else
-                    return 1;
-                n++;
-            }
-        }
-
-    return (n != 1);
+static int bad_format_check(const char *pattern, char *fmt) {
+    GError *gerr = NULL;
+    GRegex *re = g_regex_new(pattern, G_REGEX_EXTENDED, 0, &gerr);
+    GMatchInfo *mi;
+    if (gerr != NULL) {
+        rrd_set_error("cannot compile regular expression: %s (%s)", gerr->message,pattern);
+        return 1;
+    }
+    int m = g_regex_match(re, fmt, 0, &mi);
+    g_match_info_free (mi);
+    g_regex_unref(re);
+    if (!m) {
+        rrd_set_error("invalid format string '%s' (should match '%s')",fmt,pattern);
+        return 1;
+    }
+    return 0;
 }
 
+#define SAFE_STRING "(?:[^%]+|%%)*"
 
-int bad_format_imginfo(
-    char *fmt)
-{
-    char     *ptr;
-    int       n = 0;
-
-    ptr = fmt;
-    while (*ptr != '\0')
-        if (*ptr++ == '%') {
-
-            /* line cannot end with percent char */
-            if (*ptr == '\0')
-                return 1;
-            /* '%%' is allowed */
-            if (*ptr == '%')
-                ptr++;
-            /* '%s', '%S' are allowed */
-            else if (*ptr == 's' || *ptr == 'S') {
-                n = 1;
-                ptr++;
-            }
-
-            /* or else '% 4lu' and such are allowed */
-            else {
-                /* optional padding character */
-                if (*ptr == ' ')
-                    ptr++;
-                /* This should take care of 'm' */
-                while (*ptr >= '0' && *ptr <= '9')
-                    ptr++;
-                /* 'lu' must follow here */
-                if (*ptr++ != 'l')
-                    return 1;
-                if (*ptr == 'u')
-                    ptr++;
-                else
-                    return 1;
-                n++;
-            }
-        }
+int bad_format_imginfo(char *fmt){
+    return bad_format_check("^" SAFE_STRING "%s" SAFE_STRING "%lu" SAFE_STRING "%lu" SAFE_STRING "$",fmt);
+}
+#define FLOAT_STRING "%[+- 0#]?[0-9]*([.][0-9]+)?l[eEfF]"
 
-    return (n != 3);
+int bad_format_axis(char *fmt){
+    return bad_format_check("^" SAFE_STRING FLOAT_STRING SAFE_STRING "$",fmt);
 }
 
+int bad_format_print(char *fmt){
+    return bad_format_check("^" SAFE_STRING FLOAT_STRING SAFE_STRING "%s" SAFE_STRING "$",fmt);
+}
 
 int vdef_parse(
     struct graph_desc_t

programming_language: C
filename: rrd_graph.h
change_type: ModificationType.MODIFY
diff: @@ -453,7 +453,11 @@ int       rrd_graph_color(
     char *,
     char *,
     int);
-int       bad_format(
+int       bad_format_axis(
+    char *);
+int       bad_format_print(
+    char *);
+int       bad_format_imginfo(
     char *);
 int       bad_format_imginfo(
     char *);

programming_language: C