diff -Nru cacti-0.8.8b+dfsg/debian/changelog cacti-0.8.8b+dfsg/debian/changelog --- cacti-0.8.8b+dfsg/debian/changelog 2014-04-06 20:01:31.000000000 +0200 +++ cacti-0.8.8b+dfsg/debian/changelog 2015-06-27 21:31:29.000000000 +0200 @@ -1,3 +1,28 @@ +cacti (0.8.8b+dfsg-5ubuntu0.1) trusty-security; urgency=medium + + * Security update (LP: #1210822): + - CVE-2015-2665 Cross-site scripting (XSS) vulnerability in Cacti + before 0.8.8d allows remote attackers to inject arbitrary web script + or HTML via unspecified vectors. + - CVE-2015-4342 SQL Injection and Location header injection from cdef + id + - CVE-2015-4454 SQL injection vulnerability in the + get_hash_graph_template function in lib/functions.php in Cacti before + 0.8.8d allows remote attackers to execute arbitrary SQL commands via + the graph_template_id parameter to graph_templates.php. + - Unassigned CVE SQL injection VN:JVN#78187936 / TN:JPCERT#98968540 + - CVE-2014-5261 Unsufficient input sanitation leads to shell command + injection possibilities + - CVE-2014-5262 Incomplete and incorrect input parsing leads to SQL + injection attack scenarios + - CVE-2014-5025 Cross Site Scripting Vulnerability + - CVE-2014-5026 Cross Site Scripting Vulnerability + - CVE-2014-5043 Cross Site Scripting Vulnerability + - CVE-2014-2327 Cross Site Request Forgery Vulnerability + - CVE-2014-4002 Cross-Site Scripting Vulnerability + + -- Paul Gevers Sat, 27 Jun 2015 14:25:12 +0200 + cacti (0.8.8b+dfsg-5) unstable; urgency=high * Fix postinst for lighttpd setups which fail on update due to diff -Nru cacti-0.8.8b+dfsg/debian/patches/CVE-2014-2327_csrf.patch cacti-0.8.8b+dfsg/debian/patches/CVE-2014-2327_csrf.patch --- cacti-0.8.8b+dfsg/debian/patches/CVE-2014-2327_csrf.patch 1970-01-01 01:00:00.000000000 +0100 +++ cacti-0.8.8b+dfsg/debian/patches/CVE-2014-2327_csrf.patch 2015-06-27 21:31:29.000000000 +0200 @@ -0,0 +1,649 @@ +Description: Until upstream rewrites every form to include such a thing + the mitigation to prevent CVE-2014-2327 is to add a token to each form + via javascript. + . + This patch and the idea is taken from: + http://forums.cacti.net/viewtopic.php?f=21&t=52335 + . + The javascript code comes from http://repo.or.cz/w/csrf-magic.git or + http://csrf.htmlpurifier.org/ and is licensed under the BSD-2clause: + Copyright (c) 2008-2013, Edward Z. Yang + All rights reserved. + . + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + . + Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + . + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + . + To prevent a php notice during installation of the cacti package Paul Gevers + added "!isset($_SERVER['REQUEST_METHOD']) || " to line 185 of csrf-magic.php. +Source: http://forums.cacti.net/download/file.php?id=29231 +Reviewed-By: Paul Gevers + +--- /dev/null ++++ b/include/csrf/csrf-magic.js +@@ -0,0 +1,186 @@ ++/** ++ * @file ++ * ++ * Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory ++ * plays nice with other JavaScript libraries, needs testing though. ++ */ ++ ++// Here are the basic overloaded method definitions ++// The wrapper must be set BEFORE onreadystatechange is written to, since ++// a bug in ActiveXObject prevents us from properly testing for it. ++CsrfMagic = function(real) { ++ // try to make it ourselves, if you didn't pass it ++ if (!real) try { real = new XMLHttpRequest; } catch (e) {;} ++ if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;} ++ if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;} ++ if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;} ++ this.csrf = real; ++ // properties ++ var csrfMagic = this; ++ real.onreadystatechange = function() { ++ csrfMagic._updateProps(); ++ return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null; ++ }; ++ csrfMagic._updateProps(); ++} ++ ++CsrfMagic.prototype = { ++ ++ open: function(method, url, async, username, password) { ++ if (method == 'POST') this.csrf_isPost = true; ++ // deal with Opera bug, thanks jQuery ++ if (username) return this.csrf_open(method, url, async, username, password); ++ else return this.csrf_open(method, url, async); ++ }, ++ csrf_open: function(method, url, async, username, password) { ++ if (username) return this.csrf.open(method, url, async, username, password); ++ else return this.csrf.open(method, url, async); ++ }, ++ ++ send: function(data) { ++ if (!this.csrf_isPost) return this.csrf_send(data); ++ prepend = csrfMagicName + '=' + csrfMagicToken + '&'; ++ if (this.csrf_purportedLength === undefined) { ++ this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length); ++ delete this.csrf_purportedLength; ++ } ++ delete this.csrf_isPost; ++ return this.csrf_send(prepend + data); ++ }, ++ csrf_send: function(data) { ++ return this.csrf.send(data); ++ }, ++ ++ setRequestHeader: function(header, value) { ++ // We have to auto-set this at the end, since we don't know how long the ++ // nonce is when added to the data. ++ if (this.csrf_isPost && header == "Content-length") { ++ this.csrf_purportedLength = value; ++ return; ++ } ++ return this.csrf_setRequestHeader(header, value); ++ }, ++ csrf_setRequestHeader: function(header, value) { ++ return this.csrf.setRequestHeader(header, value); ++ }, ++ ++ abort: function() { ++ return this.csrf.abort(); ++ }, ++ getAllResponseHeaders: function() { ++ return this.csrf.getAllResponseHeaders(); ++ }, ++ getResponseHeader: function(header) { ++ return this.csrf.getResponseHeader(header); ++ } // , ++} ++ ++// proprietary ++CsrfMagic.prototype._updateProps = function() { ++ this.readyState = this.csrf.readyState; ++ if (this.readyState == 4) { ++ this.responseText = this.csrf.responseText; ++ this.responseXML = this.csrf.responseXML; ++ this.status = this.csrf.status; ++ this.statusText = this.csrf.statusText; ++ } ++} ++CsrfMagic.process = function(base) { ++ var prepend = csrfMagicName + '=' + csrfMagicToken; ++ if (base) return prepend + '&' + base; ++ return prepend; ++} ++// callback function for when everything on the page has loaded ++CsrfMagic.end = function() { ++ // This rewrites forms AGAIN, so in case buffering didn't work this ++ // certainly will. ++ forms = document.getElementsByTagName('form'); ++ for (var i = 0; i < forms.length; i++) { ++ form = forms[i]; ++ if (form.method.toUpperCase() !== 'POST') continue; ++ if (form.elements[csrfMagicName]) continue; ++ var input = document.createElement('input'); ++ input.setAttribute('name', csrfMagicName); ++ input.setAttribute('value', csrfMagicToken); ++ input.setAttribute('type', 'hidden'); ++ form.appendChild(input); ++ } ++} ++ ++// Sets things up for Mozilla/Opera/nice browsers ++// We very specifically match against Internet Explorer, since they haven't ++// implemented prototypes correctly yet. ++if (window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v') { ++ var x = XMLHttpRequest.prototype; ++ var c = CsrfMagic.prototype; ++ ++ // Save the original functions ++ x.csrf_open = x.open; ++ x.csrf_send = x.send; ++ x.csrf_setRequestHeader = x.setRequestHeader; ++ ++ // Notice that CsrfMagic is itself an instantiatable object, but only ++ // open, send and setRequestHeader are necessary as decorators. ++ x.open = c.open; ++ x.send = c.send; ++ x.setRequestHeader = c.setRequestHeader; ++} else { ++ // The only way we can do this is by modifying a library you have been ++ // using. We support YUI, script.aculo.us, prototype, MooTools, ++ // jQuery, Ext and Dojo. ++ if (window.jQuery) { ++ // jQuery didn't implement a new XMLHttpRequest function, so we have ++ // to do this the hard way. ++ jQuery.csrf_ajax = jQuery.ajax; ++ jQuery.ajax = function( s ) { ++ if (s.type && s.type.toUpperCase() == 'POST') { ++ s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s)); ++ if ( s.data && s.processData && typeof s.data != "string" ) { ++ s.data = jQuery.param(s.data); ++ } ++ s.data = CsrfMagic.process(s.data); ++ } ++ return jQuery.csrf_ajax( s ); ++ } ++ } ++ if (window.Prototype) { ++ // This works for script.aculo.us too ++ Ajax.csrf_getTransport = Ajax.getTransport; ++ Ajax.getTransport = function() { ++ return new CsrfMagic(Ajax.csrf_getTransport()); ++ } ++ } ++ if (window.MooTools) { ++ Browser.csrf_Request = Browser.Request; ++ Browser.Request = function () { ++ return new CsrfMagic(Browser.csrf_Request()); ++ } ++ } ++ if (window.YAHOO) { ++ // old YUI API ++ YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject; ++ YAHOO.util.Connect.createXhrObject = function (transaction) { ++ obj = YAHOO.util.Connect.csrf_createXhrObject(transaction); ++ obj.conn = new CsrfMagic(obj.conn); ++ return obj; ++ } ++ } ++ if (window.Ext) { ++ // Ext can use other js libraries as loaders, so it has to come last ++ // Ext's implementation is pretty identical to Yahoo's, but we duplicate ++ // it for comprehensiveness's sake. ++ Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject; ++ Ext.lib.Ajax.createXhrObject = function (transaction) { ++ obj = Ext.lib.Ajax.csrf_createXhrObject(transaction); ++ obj.conn = new CsrfMagic(obj.conn); ++ return obj; ++ } ++ } ++ if (window.dojo) { ++ // NOTE: this doesn't work with latest dojo ++ dojo.csrf__xhrObj = dojo._xhrObj; ++ dojo._xhrObj = function () { ++ return new CsrfMagic(dojo.csrf__xhrObj()); ++ } ++ } ++} +--- /dev/null ++++ b/include/csrf/csrf-magic.php +@@ -0,0 +1,403 @@ ++ ++ */ ++$GLOBALS['csrf']['input-name'] = '__csrf_magic'; ++ ++/** ++ * Set this to false if your site must work inside of frame/iframe elements, ++ * but do so at your own risk: this configuration protects you against CSS ++ * overlay attacks that defeat tokens. ++ */ ++$GLOBALS['csrf']['frame-breaker'] = true; ++ ++/** ++ * Whether or not CSRF Magic should be allowed to start a new session in order ++ * to determine the key. ++ */ ++$GLOBALS['csrf']['auto-session'] = true; ++ ++/** ++ * Whether or not csrf-magic should produce XHTML style tags. ++ */ ++$GLOBALS['csrf']['xhtml'] = true; ++ ++// FUNCTIONS: ++ ++// Don't edit this! ++$GLOBALS['csrf']['version'] = '1.0.4'; ++ ++/** ++ * Rewrites
on the fly to add CSRF tokens to them. This can also ++ * inject our JavaScript library. ++ */ ++function csrf_ob_handler($buffer, $flags) { ++ // Even though the user told us to rewrite, we should do a quick heuristic ++ // to check if the page is *actually* HTML. We don't begin rewriting until ++ // we hit the first "; ++ $buffer = preg_replace('#(]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer); ++ if ($GLOBALS['csrf']['frame-breaker']) { ++ $buffer = str_ireplace('', '', $buffer); ++ } ++ if ($js = $GLOBALS['csrf']['rewrite-js']) { ++ $buffer = str_ireplace( ++ '', ++ ''. ++ '', ++ $buffer ++ ); ++ $script = ''; ++ $buffer = str_ireplace('', $script . '', $buffer, $count); ++ if (!$count) { ++ $buffer .= $script; ++ } ++ } ++ return $buffer; ++} ++ ++/** ++ * Checks if this is a post request, and if it is, checks if the nonce is valid. ++ * @param bool $fatal Whether or not to fatally error out if there is a problem. ++ * @return True if check passes or is not necessary, false if failure. ++ */ ++function csrf_check($fatal = true) { ++ if (!isset($_SERVER['REQUEST_METHOD']) || ($_SERVER['REQUEST_METHOD'] !== 'POST')) return true; ++ csrf_start(); ++ $name = $GLOBALS['csrf']['input-name']; ++ $ok = false; ++ $tokens = ''; ++ do { ++ if (!isset($_POST[$name])) break; ++ // we don't regenerate a token and check it because some token creation ++ // schemes are volatile. ++ $tokens = $_POST[$name]; ++ if (!csrf_check_tokens($tokens)) break; ++ $ok = true; ++ } while (false); ++ if ($fatal && !$ok) { ++ $callback = $GLOBALS['csrf']['callback']; ++ if (trim($tokens, 'A..Za..z0..9:;,') !== '') $tokens = 'hidden'; ++ $callback($tokens); ++ exit; ++ } ++ return $ok; ++} ++ ++/** ++ * Retrieves a valid token(s) for a particular context. Tokens are separated ++ * by semicolons. ++ */ ++function csrf_get_tokens() { ++ $has_cookies = !empty($_COOKIE); ++ ++ // $ip implements a composite key, which is sent if the user hasn't sent ++ // any cookies. It may or may not be used, depending on whether or not ++ // the cookies "stick" ++ $secret = csrf_get_secret(); ++ if (!$has_cookies && $secret) { ++ // :TODO: Harden this against proxy-spoofing attacks ++ $ip = ';ip:' . csrf_hash($_SERVER['IP_ADDRESS']); ++ } else { ++ $ip = ''; ++ } ++ csrf_start(); ++ ++ // These are "strong" algorithms that don't require per se a secret ++ if (session_id()) return 'sid:' . csrf_hash(session_id()) . $ip; ++ if ($GLOBALS['csrf']['cookie']) { ++ $val = csrf_generate_secret(); ++ setcookie($GLOBALS['csrf']['cookie'], $val); ++ return 'cookie:' . csrf_hash($val) . $ip; ++ } ++ if ($GLOBALS['csrf']['key']) return 'key:' . csrf_hash($GLOBALS['csrf']['key']) . $ip; ++ // These further algorithms require a server-side secret ++ if (!$secret) return 'invalid'; ++ if ($GLOBALS['csrf']['user'] !== false) { ++ return 'user:' . csrf_hash($GLOBALS['csrf']['user']); ++ } ++ if ($GLOBALS['csrf']['allow-ip']) { ++ return ltrim($ip, ';'); ++ } ++ return 'invalid'; ++} ++ ++function csrf_flattenpost($data) { ++ $ret = array(); ++ foreach($data as $n => $v) { ++ $ret = array_merge($ret, csrf_flattenpost2(1, $n, $v)); ++ } ++ return $ret; ++} ++function csrf_flattenpost2($level, $key, $data) { ++ if(!is_array($data)) return array($key => $data); ++ $ret = array(); ++ foreach($data as $n => $v) { ++ $nk = $level >= 1 ? $key."[$n]" : "[$n]"; ++ $ret = array_merge($ret, csrf_flattenpost2($level+1, $nk, $v)); ++ } ++ return $ret; ++} ++ ++/** ++ * @param $tokens is safe for HTML consumption ++ */ ++function csrf_callback($tokens) { ++ // (yes, $tokens is safe to echo without escaping) ++ header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); ++ $data = ''; ++ foreach (csrf_flattenpost($_POST) as $key => $value) { ++ if ($key == $GLOBALS['csrf']['input-name']) continue; ++ $data .= ''; ++ } ++ echo "CSRF check failed ++ ++

CSRF check failed. Your form session may have expired, or you may not have ++ cookies enabled.

++ $data ++

Debug: $tokens

++"; ++} ++ ++/** ++ * Checks if a composite token is valid. Outward facing code should use this ++ * instead of csrf_check_token() ++ */ ++function csrf_check_tokens($tokens) { ++ if (is_string($tokens)) $tokens = explode(';', $tokens); ++ foreach ($tokens as $token) { ++ if (csrf_check_token($token)) return true; ++ } ++ return false; ++} ++ ++/** ++ * Checks if a token is valid. ++ */ ++function csrf_check_token($token) { ++ if (strpos($token, ':') === false) return false; ++ list($type, $value) = explode(':', $token, 2); ++ if (strpos($value, ',') === false) return false; ++ list($x, $time) = explode(',', $token, 2); ++ if ($GLOBALS['csrf']['expires']) { ++ if (time() > $time + $GLOBALS['csrf']['expires']) return false; ++ } ++ switch ($type) { ++ case 'sid': ++ return $value === csrf_hash(session_id(), $time); ++ case 'cookie': ++ $n = $GLOBALS['csrf']['cookie']; ++ if (!$n) return false; ++ if (!isset($_COOKIE[$n])) return false; ++ return $value === csrf_hash($_COOKIE[$n], $time); ++ case 'key': ++ if (!$GLOBALS['csrf']['key']) return false; ++ return $value === csrf_hash($GLOBALS['csrf']['key'], $time); ++ // We could disable these 'weaker' checks if 'key' was set, but ++ // that doesn't make me feel good then about the cookie-based ++ // implementation. ++ case 'user': ++ if (!csrf_get_secret()) return false; ++ if ($GLOBALS['csrf']['user'] === false) return false; ++ return $value === csrf_hash($GLOBALS['csrf']['user'], $time); ++ case 'ip': ++ if (!csrf_get_secret()) return false; ++ // do not allow IP-based checks if the username is set, or if ++ // the browser sent cookies ++ if ($GLOBALS['csrf']['user'] !== false) return false; ++ if (!empty($_COOKIE)) return false; ++ if (!$GLOBALS['csrf']['allow-ip']) return false; ++ return $value === csrf_hash($_SERVER['IP_ADDRESS'], $time); ++ } ++ return false; ++} ++ ++/** ++ * Sets a configuration value. ++ */ ++function csrf_conf($key, $val) { ++ if (!isset($GLOBALS['csrf'][$key])) { ++ trigger_error('No such configuration ' . $key, E_USER_WARNING); ++ return; ++ } ++ $GLOBALS['csrf'][$key] = $val; ++} ++ ++/** ++ * Starts a session if we're allowed to. ++ */ ++function csrf_start() { ++ if ($GLOBALS['csrf']['auto-session'] && !session_id()) { ++ session_start(); ++ } ++} ++ ++/** ++ * Retrieves the secret, and generates one if necessary. ++ */ ++function csrf_get_secret() { ++ if ($GLOBALS['csrf']['secret']) return $GLOBALS['csrf']['secret']; ++ $dir = dirname(__FILE__); ++ $file = $dir . '/csrf-secret.php'; ++ $secret = ''; ++ if (file_exists($file)) { ++ include $file; ++ return $secret; ++ } ++ if (is_writable($dir)) { ++ $secret = csrf_generate_secret(); ++ $fh = fopen($file, 'w'); ++ fwrite($fh, ' + +--- a/data_sources.php ++++ b/data_sources.php +@@ -427,7 +427,7 @@ + input_validate_input_number($matches[1]); + /* ==================================================== */ + +- $ds_list .= "
  • " . get_data_source_title($matches[1]) . "
    "; ++ $ds_list .= "
  • " . htmlspecialchars(get_data_source_title($matches[1])) . "
    "; + $ds_array[$i] = $matches[1]; + + $i++; +@@ -1359,7 +1359,7 @@ + $poller_interval = ((isset($poller_intervals[$data_source["local_data_id"]])) ? $poller_intervals[$data_source["local_data_id"]] : 0); + + form_alternate_row_color($colors["alternate"], $colors["light"], $i, 'line' . $data_source["local_data_id"]); $i++; +- form_selectable_cell("" . ((get_request_var_request("filter") != "") ? preg_replace("/(" . preg_quote(get_request_var_request("filter"), "/") . ")/i", "\\1", title_trim(htmlspecialchars($data_source["name_cache"]), read_config_option("max_title_data_source"))) : title_trim(htmlspecialchars($data_source["name_cache"]), read_config_option("max_title_data_source"))) . "", $data_source["local_data_id"]); ++ form_selectable_cell("" . ((get_request_var_request("filter") != "") ? preg_replace("/(" . preg_quote(get_request_var_request("filter"), "/") . ")/i", "\\1", title_trim(htmlspecialchars($data_source["name_cache"]), read_config_option("max_title_data_source"))) : title_trim(htmlspecialchars($data_source["name_cache"]), read_config_option("max_title_data_source"))) . "", $data_source["local_data_id"]); + form_selectable_cell($data_source['local_data_id'], $data_source['local_data_id']); + form_selectable_cell($data_input_name, $data_source["local_data_id"]); + form_selectable_cell(get_poller_interval($poller_interval), $data_source["local_data_id"]); +--- a/cdef.php ++++ b/cdef.php +@@ -194,7 +194,7 @@ + input_validate_input_number($matches[1]); + /* ==================================================== */ + +- $cdef_list .= "
  • " . db_fetch_cell("select name from cdef where id=" . $matches[1]) . "
    "; ++ $cdef_list .= "
  • " . htmlspecialchars(db_fetch_cell("select name from cdef where id=" . $matches[1])) . "
    "; + $cdef_array[$i] = $matches[1]; + + $i++; +--- a/tree.php ++++ b/tree.php +@@ -354,7 +354,7 @@ + } + + include("./include/top_header.php"); +- form_confirm("Are You Sure?", $text, htmlspecialchars("tree.php?action=edit&id=" . $_GET["tree_id"]), htmlspecialchars("tree.php?action=item_remove&id=" . $_GET["id"] . "&tree_id=" . $_GET["tree_id"])); ++ form_confirm("Are You Sure?", htmlspecialchars($text, ENT_QUOTES), htmlspecialchars("tree.php?action=edit&id=" . $_GET["tree_id"]), htmlspecialchars("tree.php?action=item_remove&id=" . $_GET["id"] . "&tree_id=" . $_GET["tree_id"])); + include("./include/bottom_footer.php"); + exit; + } +@@ -383,7 +383,7 @@ + + if ((read_config_option("deletion_verification") == "on") && (!isset($_GET["confirm"]))) { + include("./include/top_header.php"); +- form_confirm("Are You Sure?", "Are you sure you want to delete the tree '" . db_fetch_cell("select name from graph_tree where id=" . $_GET["id"]) . "'?", htmlspecialchars("tree.php"), htmlspecialchars("tree.php?action=remove&id=" . $_GET["id"])); ++ form_confirm("Are You Sure?", "Are you sure you want to delete the tree '" . htmlspecialchars(db_fetch_cell("select name from graph_tree where id=" . $_GET["id"]), ENT_QUOTES) . "'?", htmlspecialchars("tree.php"), htmlspecialchars("tree.php?action=remove&id=" . $_GET["id"])); + include("./include/bottom_footer.php"); + exit; + } +--- a/data_input.php ++++ b/data_input.php +@@ -185,7 +185,7 @@ + input_validate_input_number($matches[1]); + /* ==================================================== */ + +- $di_list .= "
  • " . db_fetch_cell("SELECT name FROM data_input WHERE id='" . $matches[1] . "'") . "
  • "; ++ $di_list .= "
  • " . htmlspecialchars(db_fetch_cell("SELECT name FROM data_input WHERE id='" . $matches[1] . "'")) . "
  • "; + $di_array[$i] = $matches[1]; + + $i++; +@@ -246,7 +246,7 @@ + + if ((read_config_option("deletion_verification") == "on") && (!isset($_GET["confirm"]))) { + include("./include/top_header.php"); +- form_confirm("Are You Sure?", "Are you sure you want to delete the field '" . db_fetch_cell("select name from data_input_fields where id=" . $_GET["id"]) . "'?", htmlspecialchars("data_input.php?action=edit&id=" . $_GET["data_input_id"]), htmlspecialchars("data_input.php?action=field_remove&id=" . $_GET["id"] . "&data_input_id=" . $_GET["data_input_id"])); ++ form_confirm("Are You Sure?", "Are you sure you want to delete the field '" . htmlspecialchars(db_fetch_cell("select name from data_input_fields where id=" . $_GET["id"]), ENT_QUOTES) . "'?", htmlspecialchars("data_input.php?action=edit&id=" . $_GET["data_input_id"]), htmlspecialchars("data_input.php?action=field_remove&id=" . $_GET["id"] . "&data_input_id=" . $_GET["data_input_id"])); + include("./include/bottom_footer.php"); + exit; + } +--- a/graphs.php ++++ b/graphs.php +@@ -387,7 +387,7 @@ + input_validate_input_number($matches[1]); + /* ==================================================== */ + +- $graph_list .= "
  • " . get_graph_title($matches[1]) . "
  • "; ++ $graph_list .= "
  • " . htmlspecialchars(get_graph_title($matches[1])) . "
  • "; + $graph_array[$i] = $matches[1]; + + $i++; +--- a/host_templates.php ++++ b/host_templates.php +@@ -156,7 +156,7 @@ + input_validate_input_number($matches[1]); + /* ==================================================== */ + +- $host_list .= "
  • " . db_fetch_cell("select name from host_template where id=" . $matches[1]) . "
    "; ++ $host_list .= "
  • " . htmlspecialchars(db_fetch_cell("select name from host_template where id=" . $matches[1])) . "
    "; + $host_array[$i] = $matches[1]; + + $i++; +--- a/data_templates.php ++++ b/data_templates.php +@@ -305,7 +305,7 @@ + input_validate_input_number($matches[1]); + /* ==================================================== */ + +- $ds_list .= "
  • " . db_fetch_cell("select name from data_template where id=" . $matches[1]) . "
    "; ++ $ds_list .= "
  • " . htmlspecialchars(db_fetch_cell("select name from data_template where id=" . $matches[1])) . "
    "; + $ds_array[$i] = $matches[1]; + + $i++; +--- a/graph_templates.php ++++ b/graph_templates.php +@@ -216,7 +216,7 @@ + input_validate_input_number($matches[1]); + /* ==================================================== */ + +- $graph_list .= "
  • " . db_fetch_cell("select name from graph_templates where id=" . $matches[1]) . "
    "; ++ $graph_list .= "
  • " . htmlspecialchars(db_fetch_cell("select name from graph_templates where id=" . $matches[1])) . "
    "; + $graph_array[$i] = $matches[1]; + + $i++; +--- a/user_admin.php ++++ b/user_admin.php +@@ -179,7 +179,7 @@ + /* ==================================================== */ + + if (get_request_var_post("drp_action") != "2") { +- $user_list .= "
  • " . db_fetch_cell("SELECT username FROM user_auth WHERE id=" . $matches[1]) . "
    "; ++ $user_list .= "
  • " . htmlspecialchars(db_fetch_cell("SELECT username FROM user_auth WHERE id=" . $matches[1])) . "
    "; + } + $user_array[$i] = $matches[1]; + +--- a/graph_templates_inputs.php ++++ b/graph_templates_inputs.php +@@ -134,7 +134,7 @@ + + if ((read_config_option("deletion_verification") == "on") && (!isset($_GET["confirm"]))) { + include("./include/top_header.php"); +- form_confirm("Are You Sure?", "Are you sure you want to delete the input item '" . db_fetch_cell("select name from graph_template_input where id=" . $_GET["id"]) . "'? NOTE: Deleting this item will NOT affect graphs that use this template.", htmlspecialchars("graph_templates.php?action=template_edit&id=" . $_GET["graph_template_id"]), htmlspecialchars("graph_templates_inputs.php?action=input_remove&id=" . $_GET["id"] . "&graph_template_id=" . $_GET["graph_template_id"])); ++ form_confirm("Are You Sure?", "Are you sure you want to delete the input item '" . htmlspecialchars(db_fetch_cell("select name from graph_template_input where id=" . $_GET["id"]), ENT_QUOTES) . "'? NOTE: Deleting this item will NOT affect graphs that use this template.", htmlspecialchars("graph_templates.php?action=template_edit&id=" . $_GET["graph_template_id"]), htmlspecialchars("graph_templates_inputs.php?action=input_remove&id=" . $_GET["id"] . "&graph_template_id=" . $_GET["graph_template_id"])); + include("./include/bottom_footer.php"); + exit; + } +--- a/data_queries.php ++++ b/data_queries.php +@@ -340,7 +340,7 @@ + + if ((read_config_option("deletion_verification") == "on") && (!isset($_GET["confirm"]))) { + include("./include/top_header.php"); +- form_confirm("Are You Sure?", "Are you sure you want to delete the Data Query Graph '" . db_fetch_cell("select name from snmp_query_graph where id=" . $_GET["id"]) . "'?", htmlspecialchars("data_queries.php?action=edit&id=" . $_GET["snmp_query_id"]), htmlspecialchars("data_queries.php?action=item_remove&id=" . $_GET["id"] . "&snmp_query_id=" . $_GET["snmp_query_id"])); ++ form_confirm("Are You Sure?", "Are you sure you want to delete the Data Query Graph '" . htmlspecialchars(db_fetch_cell("select name from snmp_query_graph where id=" . $_GET["id"]), ENT_QUOTES) . "'?", htmlspecialchars("data_queries.php?action=edit&id=" . $_GET["snmp_query_id"]), htmlspecialchars("data_queries.php?action=item_remove&id=" . $_GET["id"] . "&snmp_query_id=" . $_GET["snmp_query_id"])); + include("./include/bottom_footer.php"); + exit; + } +--- a/utilities.php ++++ b/utilities.php +@@ -746,7 +746,7 @@ + + + \\1", $item["full_name"])) : $item["full_name"]); ++ print (strlen(get_request_var_request("filter")) ? (preg_replace("/(" . preg_quote(get_request_var_request("filter"), "/") . ")/i", "\\1", htmlspecialchars($item["full_name"]))) : htmlspecialchars($item["full_name"])); + }else{ + print "(User Removed)"; + } diff -Nru cacti-0.8.8b+dfsg/debian/patches/CVE-2014-5261_shell_metachar.patch cacti-0.8.8b+dfsg/debian/patches/CVE-2014-5261_shell_metachar.patch --- cacti-0.8.8b+dfsg/debian/patches/CVE-2014-5261_shell_metachar.patch 1970-01-01 01:00:00.000000000 +0100 +++ cacti-0.8.8b+dfsg/debian/patches/CVE-2014-5261_shell_metachar.patch 2015-06-27 21:31:29.000000000 +0200 @@ -0,0 +1,19 @@ +Description: CVE-2014-5261 Unsufficient input sanitation + Since there is no check whether $size is actually a number, only that + it starts with a number ... it's possible to insert commands by adding + a ';' followed by any command. +Author: cigamit@cacti.net +Bug: http://bugs.cacti.net/view.php?id=2455 +Bug-Redhat: https://bugzilla.redhat.com/show_bug.cgi?id=1127165 +Source: svn://svn.cacti.net/cacti/cacti@7454 +--- a/lib/rrd.php ++++ b/lib/rrd.php +@@ -2060,7 +2060,7 @@ + $size = 8; + } + +- return "--font " . strtoupper($type) . ":" . $size . ":" . $font . RRD_NL; ++ return "--font " . strtoupper($type) . ":" . floatval($size) . ":" . $font . RRD_NL; + } + + function rrd_substitute_host_query_data($txt_graph_item, $graph, $graph_item) { diff -Nru cacti-0.8.8b+dfsg/debian/patches/CVE-2014-5262_SQL_injection.patch cacti-0.8.8b+dfsg/debian/patches/CVE-2014-5262_SQL_injection.patch --- cacti-0.8.8b+dfsg/debian/patches/CVE-2014-5262_SQL_injection.patch 1970-01-01 01:00:00.000000000 +0100 +++ cacti-0.8.8b+dfsg/debian/patches/CVE-2014-5262_SQL_injection.patch 2015-06-27 20:41:30.000000000 +0200 @@ -0,0 +1,19 @@ +Description: CVE-2014-5262 Incomplete and incorrect input parsing + leads to SQL injection attack scenarios +Author: cigamit@cacti.net +Bug: http://bugs.cacti.net/view.php?id=2455 +Bug-Redhat: https://bugzilla.redhat.com/show_bug.cgi?id=1127165 +Source: svn://svn.cacti.net/cacti/cacti@7454 +--- a/graph_settings.php ++++ b/graph_settings.php +@@ -54,6 +54,10 @@ + + while (list($tab_short_name, $tab_fields) = each($settings_graphs)) { + while (list($field_name, $field_array) = each($tab_fields)) { ++ /* Check every field with a numeric default value and reset it to default if the inputted value is not numeric */ ++ if (isset($field_array["default"]) && is_numeric($field_array["default"]) && !is_numeric(get_request_var_post($field_name))) { ++ $_POST[$field_name] = $field_array["default"]; ++ } + if ($field_array["method"] == "checkbox") { + if (isset($_POST[$field_name])) { + db_execute("REPLACE INTO settings_graphs (user_id,name,value) VALUES (" . $_SESSION["sess_user_id"] . ",'$field_name', 'on')"); diff -Nru cacti-0.8.8b+dfsg/debian/patches/CVE-2015-2665_XSS_in_graphs.php.patch cacti-0.8.8b+dfsg/debian/patches/CVE-2015-2665_XSS_in_graphs.php.patch --- cacti-0.8.8b+dfsg/debian/patches/CVE-2015-2665_XSS_in_graphs.php.patch 1970-01-01 01:00:00.000000000 +0100 +++ cacti-0.8.8b+dfsg/debian/patches/CVE-2015-2665_XSS_in_graphs.php.patch 2015-06-27 21:31:29.000000000 +0200 @@ -0,0 +1,18 @@ +Description: CVE-2015-2665 Cross-site scripting (XSS) vulnerability in Cacti + before 0.8.8d allows remote attackers to inject arbitrary web script + or HTML via unspecified vectors. +Source: http://svn.cacti.net/viewvc/cacti/tags/0.8.8d/graphs.php?r1=7716&r2=7717&view=patch + +Index: cacti/graphs.php +=================================================================== +--- cacti.orig/graphs.php ++++ cacti/graphs.php +@@ -1334,7 +1334,7 @@ function graph() { + /* we're escaping strings here, so no need to escape them on form_selectable_cell */ + $template_name = ((empty($graph["name"])) ? "None" : htmlspecialchars($graph["name"])); + form_alternate_row_color($colors["alternate"], $colors["light"], $i, 'line' . $graph["local_graph_id"]); $i++; +- form_selectable_cell("" . ((get_request_var_request("filter") != "") ? preg_replace("/(" . preg_quote(get_request_var_request("filter"), "/") . ")/i", "\\1", title_trim(htmlspecialchars($graph["title_cache"]), read_config_option("max_title_graph"))) : title_trim(htmlspecialchars($graph["title_cache"]), read_config_option("max_title_graph"))) . "", $graph["local_graph_id"]); ++ form_selectable_cell("" . ((get_request_var_request("filter") != "") ? preg_replace("/(" . preg_quote(get_request_var_request("filter"), "/") . ")/i", "\\1", title_trim(htmlspecialchars($graph["title_cache"]), read_config_option("max_title_graph"))) : title_trim(htmlspecialchars($graph["title_cache"]), read_config_option("max_title_graph"))) . "", $graph["local_graph_id"]); + form_selectable_cell($graph["local_graph_id"], $graph["local_graph_id"]); + form_selectable_cell(((get_request_var_request("filter") != "") ? preg_replace("/(" . preg_quote(get_request_var_request("filter"), "/") . ")/i", "\\1", $template_name) : $template_name), $graph["local_graph_id"]); + form_selectable_cell($graph["height"] . "x" . $graph["width"], $graph["local_graph_id"]); diff -Nru cacti-0.8.8b+dfsg/debian/patches/CVE-2015-4342_SQL_injection_in_cdef.php.patch cacti-0.8.8b+dfsg/debian/patches/CVE-2015-4342_SQL_injection_in_cdef.php.patch --- cacti-0.8.8b+dfsg/debian/patches/CVE-2015-4342_SQL_injection_in_cdef.php.patch 1970-01-01 01:00:00.000000000 +0100 +++ cacti-0.8.8b+dfsg/debian/patches/CVE-2015-4342_SQL_injection_in_cdef.php.patch 2015-06-27 21:31:29.000000000 +0200 @@ -0,0 +1,39 @@ +Description: CVE-2015-4342 SQL Injection and Location header injection + from cdef id +Bug: http://bugs.cacti.net/view.php?id=2571 +Source: http://svn.cacti.net/viewvc?view=rev&revision=7719 + +Index: cacti/lib/functions.php +=================================================================== +--- cacti.orig/lib/functions.php ++++ cacti/lib/functions.php +@@ -1987,6 +1987,9 @@ function get_hash_data_input($data_input + @arg $sub_type (optional) return the hash for a particlar sub-type of this type + @returns - a 128-bit, hexadecimal hash */ + function get_hash_cdef($cdef_id, $sub_type = "cdef") { ++ if (! is_numeric($cdef_id)) { ++ $cdef_id = -1; ++ } + if ($sub_type == "cdef") { + $hash = db_fetch_cell("select hash from cdef where id=$cdef_id"); + }elseif ($sub_type == "cdef_item") { +Index: cacti/cdef.php +=================================================================== +--- cacti.orig/cdef.php ++++ cacti/cdef.php +@@ -107,6 +107,15 @@ function draw_cdef_preview($cdef_id) { + -------------------------- */ + + function form_save() { ++ ++ // make sure ids are numeric ++ if (isset($_POST["id"]) && ! is_numeric($_POST["id"])) { ++ $_POST["id"] = 0; ++ } ++ if (isset($_POST["cdef_id"]) && ! is_numeric($_POST["cdef_id"])) { ++ $_POST["cdef_id"] = 0; ++ } ++ + if (isset($_POST["save_component_cdef"])) { + $save["id"] = $_POST["id"]; + $save["hash"] = get_hash_cdef($_POST["id"]); diff -Nru cacti-0.8.8b+dfsg/debian/patches/CVE-2015-4454_SQL_injection_in_get_hash_graph_template.patch cacti-0.8.8b+dfsg/debian/patches/CVE-2015-4454_SQL_injection_in_get_hash_graph_template.patch --- cacti-0.8.8b+dfsg/debian/patches/CVE-2015-4454_SQL_injection_in_get_hash_graph_template.patch 1970-01-01 01:00:00.000000000 +0100 +++ cacti-0.8.8b+dfsg/debian/patches/CVE-2015-4454_SQL_injection_in_get_hash_graph_template.patch 2015-06-27 20:41:30.000000000 +0200 @@ -0,0 +1,24 @@ +Description: CVE-2015-4454 SQL injection vulnerability in the + get_hash_graph_template function in lib/functions.php in Cacti before + 0.8.8d allows remote attackers to execute arbitrary SQL commands via + the graph_template_id parameter to graph_templates.php. +Bug: http://bugs.cacti.net/view.php?id=2572 +Source: http://svn.cacti.net/viewvc?view=rev&revision=7720 + +Index: cacti/graph_templates.php +=================================================================== +--- cacti.orig/graph_templates.php ++++ cacti/graph_templates.php +@@ -85,6 +85,12 @@ switch ($_REQUEST["action"]) { + -------------------------- */ + + function form_save() { ++ ++ // sanitize ids ++ if (isset($_POST["graph_template_id"]) && ! is_numeric($_POST["graph_template_id"])) { ++ $_POST["graph_template_id"] = 0; ++ } ++ + if (isset($_POST["save_component_template"])) { + $save1["id"] = $_POST["graph_template_id"]; + $save1["hash"] = get_hash_graph_template($_POST["graph_template_id"]); diff -Nru cacti-0.8.8b+dfsg/debian/patches/CVE-2015-XXXX_SQL_injection_in_tab.patch cacti-0.8.8b+dfsg/debian/patches/CVE-2015-XXXX_SQL_injection_in_tab.patch --- cacti-0.8.8b+dfsg/debian/patches/CVE-2015-XXXX_SQL_injection_in_tab.patch 1970-01-01 01:00:00.000000000 +0100 +++ cacti-0.8.8b+dfsg/debian/patches/CVE-2015-XXXX_SQL_injection_in_tab.patch 2015-06-27 20:41:30.000000000 +0200 @@ -0,0 +1,20 @@ +Description: Unassigned CVE SQL injection vulnerability + reported via VN:JVN#78187936 / TN:JPCERT#98968540 +Source: http://svn.cacti.net/viewvc?view=rev&revision=7718 + +Index: cacti/settings.php +=================================================================== +--- cacti.orig/settings.php ++++ cacti/settings.php +@@ -78,6 +78,11 @@ case 'save': + default: + include("./include/top_header.php"); + ++ /* clean up tab string */ ++ if (isset($_REQUEST['tab'])) { ++ $_REQUEST['tab'] = sanitize_search_string(get_request_var_request('tab')); ++ } ++ + /* set the default settings category */ + if (!isset($_GET["tab"])) { + /* there is no selected tab; select the first one */ diff -Nru cacti-0.8.8b+dfsg/debian/patches/series cacti-0.8.8b+dfsg/debian/patches/series --- cacti-0.8.8b+dfsg/debian/patches/series 2014-04-05 13:00:53.000000000 +0200 +++ cacti-0.8.8b+dfsg/debian/patches/series 2015-06-27 21:31:29.000000000 +0200 @@ -15,3 +15,12 @@ fix_cross_site_scripting_CVE-2013-5588.patch upgrade_script_misses_latest_releases.patch fix_four_CVE-2014_issues.patch +CVE-2014-2327_csrf.patch +CVE-2014-4002_css_input_sanitation.patch +CVE-2014-5026_XSS_in_item_names.patch +CVE-2014-5261_shell_metachar.patch +CVE-2014-5262_SQL_injection.patch +CVE-2015-2665_XSS_in_graphs.php.patch +CVE-2015-4342_SQL_injection_in_cdef.php.patch +CVE-2015-4454_SQL_injection_in_get_hash_graph_template.patch +CVE-2015-XXXX_SQL_injection_in_tab.patch