CVE-2019-11455: Out-of-bounds Read (CWE-125)
value: A buffer over-read in Util_urlDecode in util.c in Tildeslash Monit before 5.25.3 allows a remote authenticated attacker to retrieve the contents of adjacent memory via manipulation of GET or POST parameters. The attacker can also cause a denial of service (application outage).
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 8.1
Published: 2019-04-22T16:29Z
Repository: https://bitbucket.org/tildeslash/monit
Language: Other
Files changed: CHANGES, src/util.c
Commit date: 2019-03-04 15:49:08 +0100
Commit message: Fixed: Buffer overrun vulnerability in URL decoding. Thanks to Zack Flack for report.

Diff: diff --git a/CHANGES b/CHANGES
index 8479413b..8996b842 100644
--- a/CHANGES
+++ b/CHANGES
@@ -28,10 +28,12 @@ MONIT_PROCESS_* variables.
 Fixed: The CPU user/system/wait statistics may show negative value if the system counter jumps back or wraps.
 
 Fixed: XSS vulnerabilitty: HTML escape the log file content when viewed via Monit GUI. Thanks for Zack Flack
 for report.
 
+Fixed: Buffer overrun vulnerability in URL decoding. Thanks to Zack Flack for report.
+
 
 Version 5.25.2
 
 New: Issue #123: Added a bash completion script for monit command-line. The script is not installed
 by default, if you want to enable the bash completion for monit, copy system/bash/monit file to
diff --git a/src/util.c b/src/util.c
index 401a9bc0..ab1b48df 100644
--- a/src/util.c
+++ b/src/util.c
@@ -232,11 +232,11 @@ static char *is_str_defined(char *s) {
 
 
 /**
  * Convert a hex char to a char
  */
-static char x2c(char *hex) {
+static char _x2c(char *hex) {
         register char digit;
         digit = ((hex[0] >= 'A') ? ((hex[0] & 0xdf) - 'A')+10 : (hex[0] - '0'));
         digit *= 16;
         digit += (hex[1] >= 'A' ? ((hex[1] & 0xdf) - 'A')+10 : (hex[1] - '0'));
         return(digit);
@@ -523,11 +523,11 @@ void Util_handleEscapes(char *buf) {
                                                          is in check_generic(). The \0x00 -> 0 byte swap is performed
                                                          there and in-place.
                                                          */
                                                         *(buf + insertpos) = *(buf+editpos);
                                                 } else {
-                                                        *(buf + insertpos) = x2c(&buf[editpos + 3]);
+                                                        *(buf + insertpos) = _x2c(&buf[editpos + 3]);
                                                         editpos += 4;
                                                 }
                                         }
                                         break;
 
@@ -559,11 +559,11 @@ int Util_handle0Escapes(char *buf) {
         for (editpos = insertpos = 0; *(buf + editpos) != '\0'; editpos++, insertpos++) {
                 if (*(buf + editpos) == '\\' ) {
                         switch (*(buf + editpos + 1)) {
                                 case '0':
                                         if (*(buf + editpos + 2) == 'x') {
-                                                *(buf + insertpos) = x2c(&buf[editpos+3]);
+                                                *(buf + insertpos) = _x2c(&buf[editpos+3]);
                                                 editpos += 4;
                                         }
                                         break;
 
                                 default:
@@ -1549,17 +1549,19 @@ char *Util_urlEncode(char *string, boolean_t isParameterValue) {
 
 char *Util_urlDecode(char *url) {
         if (url && *url) {
                 register int x, y;
                 for (x = 0, y = 0; url[y]; x++, y++) {
-                        if ((url[x] = url[y]) == '+')
+                        if (url[y] == '+') {
                                 url[x] = ' ';
-                        else if (url[x] == '%') {
-                                if (! (url[x + 1] && url[x + 2]))
+                        } else if (url[y] == '%') {
+                                if (! url[y + 1] || ! url[y + 2])
                                         break;
-                                url[x] = x2c(url + y + 1);
+                                url[x] = _x2c(url + y + 1);
                                 y += 2;
+                        } else {
+                                url[x] = url[y];
                         }
                 }
                 url[x] = 0;
         }
         return url;

Vulnerable code: static char x2c(char *hex) {
                                                        *(buf + insertpos) = x2c(&buf[editpos + 3]);
                                                *(buf + insertpos) = x2c(&buf[editpos+3]);
                        if ((url[x] = url[y]) == '+')
                        else if (url[x] == '%') {
                                if (! (url[x + 1] && url[x + 2]))
                                url[x] = x2c(url + y + 1);
Fixed code: Fixed: Buffer overrun vulnerability in URL decoding. Thanks to Zack Flack for report.

static char _x2c(char *hex) {
                                                        *(buf + insertpos) = _x2c(&buf[editpos + 3]);
                                                *(buf + insertpos) = _x2c(&buf[editpos+3]);
                        if (url[y] == '+') {
                        } else if (url[y] == '%') {
                                if (! url[y + 1] || ! url[y + 2])
                                url[x] = _x2c(url + y + 1);
                        } else {
                                url[x] = url[y];