From 0ee8451fc19a2ed5c920b856551d5d8f41d92051 Mon Sep 17 00:00:00 2001 From: sc979 <34628915+sc979@users.noreply.github.com> Date: Tue, 29 Oct 2019 17:38:11 +0100 Subject: [PATCH] fix(secu): Avoid SQL injections in service by servicegroup pages (#8065) * fix(secu): avoid SQL injection in serviceByServicegroupGridXML.php file * fix(secu): avoid SQL injection in serviceByServicegroupSummaryXML.php file * fix(secu): remove or sanitize unused https arguments in service by servicegroup GRID (#8066) --- .../xml/serviceGridBySGXML.php | 129 ++++++++++-------- .../xml/serviceSummaryBySGXML.php | 55 ++++++-- 2 files changed, 119 insertions(+), 65 deletions(-) diff --git a/www/include/monitoring/status/ServicesServiceGroups/xml/serviceGridBySGXML.php b/www/include/monitoring/status/ServicesServiceGroups/xml/serviceGridBySGXML.php index c95d8b1508c..9e5c8b21efe 100644 --- a/www/include/monitoring/status/ServicesServiceGroups/xml/serviceGridBySGXML.php +++ b/www/include/monitoring/status/ServicesServiceGroups/xml/serviceGridBySGXML.php @@ -35,18 +35,15 @@ ini_set("display_errors", "Off"); -require_once realpath(__DIR__ . "/../../../../../../config/centreon.config.php"); require_once realpath(__DIR__ . "/../../../../../../bootstrap.php"); - include_once _CENTREON_PATH_ . "www/class/centreonUtils.class.php"; - include_once _CENTREON_PATH_ . "www/class/centreonXMLBGRequest.class.php"; include_once _CENTREON_PATH_ . "www/include/monitoring/status/Common/common-Func.php"; include_once _CENTREON_PATH_ . "www/include/common/common-Func.php"; include_once _CENTREON_PATH_ . "www/class/centreonService.class.php"; // Create XML Request Objects - CentreonSession::start(); +CentreonSession::start(); $obj = new CentreonXMLBGRequest($dependencyInjector, session_id(), 1, 1, 0, 1); $svcObj = new CentreonService($obj->DB); @@ -59,19 +56,30 @@ $obj->getDefaultFilters(); // Check Arguments From GET tab -$o = $obj->checkArgument("o", $_GET, "h"); -$p = $obj->checkArgument("p", $_GET, "2"); -$nc = $obj->checkArgument("nc", $_GET, "0"); -$num = $obj->checkArgument("num", $_GET, 0); -$limit = $obj->checkArgument("limit", $_GET, 20); -$instance = $obj->checkArgument("instance", $_GET, $obj->defaultPoller); -$hostgroups = $obj->checkArgument("hostgroups", $_GET, $obj->defaultHostgroups); -$hSearch = $obj->checkArgument("host_search", $_GET, ""); -$sgSearch = $obj->checkArgument("sg_search", $_GET, ""); -$sort_type = $obj->checkArgument("sort_type", $_GET, "host_name"); -$order = $obj->checkArgument("order", $_GET, "ASC"); -$dateFormat = $obj->checkArgument("date_time_format_status", $_GET, "Y/m/d H:i:s"); -$queryValues = array(); +$o = filter_input(INPUT_GET, 'o', FILTER_SANITIZE_STRING, array('options' => array('default' => 'h'))); +$p = filter_input(INPUT_GET, 'p', FILTER_VALIDATE_INT, array('options' => array('default' => 2))); +$num = filter_input(INPUT_GET, 'num', FILTER_VALIDATE_INT, array('options' => array('default' => 0))); +$limit = filter_input(INPUT_GET, 'limit', FILTER_VALIDATE_INT, array('options' => array('default' => 20))); +//if instance value is not set, displaying all active pollers linked resources +$instance = filter_var($obj->defaultPoller ?? -1, FILTER_VALIDATE_INT); +$hSearch = filter_input(INPUT_GET, 'host_search', FILTER_SANITIZE_STRING, array('options' => array('default' => ''))); +$sgSearch = filter_input(INPUT_GET, 'sg_search', FILTER_SANITIZE_STRING, array('options' => array('default' => ''))); +$sort_type = filter_input( + INPUT_GET, + 'sort_type', + FILTER_SANITIZE_STRING, + array('options' => array('default' => 'host_name')) +); +$order = filter_input( + INPUT_GET, + 'order', + FILTER_VALIDATE_REGEXP, + array('options' => array('default' => 'ASC', 'regexp' => '/^(ASC|DESC)$/')) +); + +//saving bound values +$queryValues = []; +$queryValues2 = []; // Backup poller selection $obj->setInstanceHistory($instance); @@ -97,16 +105,16 @@ } // this query allows to manage pagination -$query = "SELECT SQL_CALC_FOUND_ROWS DISTINCT sg.servicegroup_id, h.host_id " - . "FROM servicegroups sg, services_servicegroups sgm, hosts h, services s "; +$query = "SELECT SQL_CALC_FOUND_ROWS DISTINCT sg.servicegroup_id, h.host_id + FROM servicegroups sg, services_servicegroups sgm, hosts h, services s "; if (!$obj->is_admin) { $query .= ", centreon_acl "; } -$query .= "WHERE sgm.servicegroup_id = sg.servicegroup_id " - . "AND sgm.host_id = h.host_id " - . "AND sgm.service_id = s.service_id "; +$query .= "WHERE sgm.servicegroup_id = sg.servicegroup_id + AND sgm.host_id = h.host_id + AND sgm.service_id = s.service_id "; // filter elements with acl (host, service, servicegroup) if (!$obj->is_admin) { @@ -119,18 +127,20 @@ // Servicegroup search if ($sgSearch != "") { - $query .= "AND sg.name = :sgSearch "; - $queryValues[':sgSearch'] = [ - PDO::PARAM_STR => $sgSearch + $query .= " AND sg.name = :sgSearch "; + $queryValues['sgSearch'] = [ + \PDO::PARAM_STR => $sgSearch ]; } // Host search $h_search = ''; if ($hSearch != "") { - $h_search .= "AND h.name like :hSearch "; - $queryValues[':hSearch'] = [ - PDO::PARAM_STR => "%" . $hSearch . "%" + $h_search .= " AND h.name LIKE :hSearch "; + // as this partial request is used in two queries, we need to bound it two times using two arrays + // to avoid incoherent number of bound variables in the second query + $queryValues['hSearch'] = $queryValues2['hSearch'] = [ + \PDO::PARAM_STR => "%" . $hSearch . "%" ]; } $query .= $h_search; @@ -141,26 +151,25 @@ // Poller search if ($instance != -1) { $query .= " AND h.instance_id = :instance "; - $queryValues[':instance'] = [ - PDO::PARAM_INT => $instance + $queryValues['instance'] = [ + \PDO::PARAM_INT => $instance ]; } -$query .= "ORDER BY sg.name " . $order - . " LIMIT :numLimit, :limit"; -$queryValues[':numLimit'] = [ - PDO::PARAM_INT => (int) ($num * $limit) +$query .= " ORDER BY sg.name " . $order . " LIMIT :numLimit, :limit"; +$queryValues['numLimit'] = [ + \PDO::PARAM_INT => (int)($num * $limit) ]; -$queryValues[':limit'] = [ - PDO::PARAM_INT => (int) $limit +$queryValues['limit'] = [ + \PDO::PARAM_INT => (int)$limit ]; -$DBRESULT = $obj->DBC->prepare($query); +$dbResult = $obj->DBC->prepare($query); foreach ($queryValues as $bindId => $bindData) { foreach ($bindData as $bindType => $bindValue) { - $DBRESULT->bindValue($bindId, $bindValue, $bindType); + $dbResult->bindValue($bindId, $bindValue, $bindType); } } -$DBRESULT->execute(); +$dbResult->execute(); $numRows = $obj->DBC->query("SELECT FOUND_ROWS()")->fetchColumn(); // Create XML Flow @@ -183,7 +192,7 @@ if ($numRows > 0) { $sg_search .= "AND ("; $servicegroups = array(); - while ($row = $DBRESULT->fetch()) { + while ($row = $dbResult->fetch()) { $servicesgroups[$row['servicegroup_id']][] = $row['host_id']; } $servicegroupsSql1 = array(); @@ -192,27 +201,33 @@ foreach ($value as $hostId) { $hostsSql[] = $hostId; } - $servicegroupsSql1[] = "(sg.servicegroup_id = " . $key . " AND h.host_id IN (" . - implode(',', $hostsSql) . ")) "; + $servicegroupsSql1[] = "(sg.servicegroup_id = " . $key . + " AND h.host_id IN (" . implode(',', $hostsSql) . ")) "; } $sg_search .= implode(" OR ", $servicegroupsSql1); $sg_search .= ") "; if ($sgSearch != "") { - $sg_search .= "AND sg.name = '" . $sgSearch . "' "; + $sg_search .= "AND sg.name = :sgSearch"; + $queryValues2['sgSearch'] = [ + \PDO::PARAM_STR => $sgSearch + ]; } - $query2 = "SELECT SQL_CALC_FOUND_ROWS DISTINCT sg.name AS sg_name, sg.name as alias, h.name as host_name, " - . "h.state as host_state, h.icon_image, h.host_id, s.state, s.description, s.service_id, " - . "(case s.state when 0 then 3 when 2 then 0 when 3 then 2 else s.state END) as tri " - . "FROM servicegroups sg, services_servicegroups sgm, services s, hosts h "; + $query2 = "SELECT SQL_CALC_FOUND_ROWS DISTINCT sg.name AS sg_name, + sg.name AS alias, + h.name AS host_name, + h.state as host_state, + h.icon_image, h.host_id, s.state, s.description, s.service_id, + (CASE s.state WHEN 0 THEN 3 WHEN 2 THEN 0 WHEN 3 THEN 2 ELSE s.state END) AS tri + FROM servicegroups sg, services_servicegroups sgm, services s, hosts h "; if (!$obj->is_admin) { $query2 .= ", centreon_acl "; } - $query2 .= "WHERE sgm.servicegroup_id = sg.servicegroup_id " - . "AND sgm.host_id = h.host_id " - . "AND sgm.service_id = s.service_id "; + $query2 .= "WHERE sgm.servicegroup_id = sg.servicegroup_id + AND sgm.host_id = h.host_id + AND sgm.service_id = s.service_id "; // filter elements with acl (host, service, servicegroup) if (!$obj->is_admin) { @@ -224,7 +239,13 @@ } $query2 .= $sg_search . $h_search . $s_search . " ORDER BY sg_name, tri ASC"; - $DBRESULT = $obj->DBC->query($query2); + $dbResult = $obj->DBC->prepare($query2); + foreach ($queryValues2 as $bindId => $bindData) { + foreach ($bindData as $bindType => $bindValue) { + $dbResult->bindValue($bindId, $bindValue, $bindType); + } + } + $dbResult->execute(); $ct = 0; $sg = ""; @@ -232,7 +253,7 @@ $flag = 0; $count = 0; - while ($tab = $DBRESULT->fetch()) { + while ($tab = $dbResult->fetch()) { if (!isset($aTab[$tab["sg_name"]])) { $aTab[$tab["sg_name"]] = array( 'sgn' => CentreonUtils::escapeSecure($tab["sg_name"]), @@ -250,13 +271,12 @@ } $aTab[$tab["sg_name"]]['host'][$tab["host_name"]] = array( 'h' => $tab["host_name"], - 'hs' => $tab["host_state"], + 'hs' => _($obj->statusHost[$tab["host_state"]]), 'hn' => CentreonUtils::escapeSecure($tab["host_name"]), 'hico' => $icone, 'hnl' => CentreonUtils::escapeSecure(urlencode($tab["host_name"])), 'hid' => $tab["host_id"], "hcount" => $count, - "hs" => _($obj->statusHost[$tab["host_state"]]), "hc" => $obj->colorHost[$tab["host_state"]], 'service' => array() ); @@ -264,7 +284,6 @@ if (!isset($aTab[$tab["sg_name"]]['host'][$tab["host_name"]]['service'][$tab['description']])) { $aTab[$tab["sg_name"]]['host'][$tab["host_name"]]['service'][$tab['description']] = array( - "sn" => CentreonUtils::escapeSecure($tab['description']), "snl" => CentreonUtils::escapeSecure(urlencode($tab['description'])), "sc" => $obj->colorService[$tab['state']], diff --git a/www/include/monitoring/status/ServicesServiceGroups/xml/serviceSummaryBySGXML.php b/www/include/monitoring/status/ServicesServiceGroups/xml/serviceSummaryBySGXML.php index c54ddf9aae0..ee6d6950e8a 100644 --- a/www/include/monitoring/status/ServicesServiceGroups/xml/serviceSummaryBySGXML.php +++ b/www/include/monitoring/status/ServicesServiceGroups/xml/serviceSummaryBySGXML.php @@ -83,6 +83,10 @@ array('options' => array('default' => 'ASC', 'regexp' => '/^(ASC|DESC)$/')) ); +//saving bound values +$queryValues = []; +$queryValues2 = []; + /* * Backup poller selection */ @@ -121,13 +125,21 @@ // Servicegroup search if ($sgSearch != "") { - $query .= "AND sg.name = '" . $sgSearch . "' "; + $query .= "AND sg.name = :sgSearch "; + $queryValues['sgSearch'] = [ + \PDO::PARAM_STR => $sgSearch + ]; } // Host search $h_search = ''; if ($hSearch != "") { - $h_search .= "AND h.name like '%" . $hSearch . "%' "; + $h_search .= " AND h.name LIKE :hSearch "; + // as this partial request is used in two queries, we need to bound it two times using two arrays + // to avoid incoherent number of bound variables in the second query + $queryValues['hSearch'] = $queryValues2['hSearch'] = [ + \PDO::PARAM_STR => "%" . $hSearch . "%" + ]; } $query .= $h_search; @@ -136,13 +148,27 @@ // Poller search if ($instance != -1) { - $query .= " AND h.instance_id = " . $instance . " "; + $query .= " AND h.instance_id = :instance "; + $queryValues['instance'] = [ + \PDO::PARAM_INT => $instance + ]; } -$query .= "ORDER BY sg.name " . $order . " LIMIT " . ($num * $limit) . ", " . $limit; - -$DBRESULT = $obj->DBC->query($query); +$query .= "ORDER BY sg.name " . $order . " LIMIT :numLimit, :limit"; +$queryValues['numLimit'] = [ + \PDO::PARAM_INT => (int)($num * $limit) +]; +$queryValues['limit'] = [ + \PDO::PARAM_INT => (int)$limit +]; +$dbResult = $obj->DBC->prepare($query); +foreach ($queryValues as $bindId => $bindData) { + foreach ($bindData as $bindType => $bindValue) { + $dbResult->bindValue($bindId, $bindValue, $bindType); + } +} +$dbResult->execute(); $numRows = $obj->DBC->query("SELECT FOUND_ROWS()")->fetchColumn(); /** @@ -170,7 +196,7 @@ if ($numRows > 0) { $sg_search .= "AND ("; $servicegroups = array(); - while ($row = $DBRESULT->fetchRow()) { + while ($row = $dbResult->fetch()) { $servicesgroups[$row['servicegroup_id']][] = $row['host_id']; } $servicegroupsSql1 = array(); @@ -185,7 +211,10 @@ $sg_search .= implode(" OR ", $servicegroupsSql1); $sg_search .= ") "; if ($sgSearch != "") { - $sg_search .= "AND sg.name = '" . $sgSearch . "' "; + $sg_search .= "AND sg.name = :sgSearch"; + $queryValues2['sgSearch'] = [ + \PDO::PARAM_STR => $sgSearch + ]; } $query2 = "SELECT SQL_CALC_FOUND_ROWS count(s.state) as count_state, @@ -204,7 +233,13 @@ . $obj->access->queryBuilder("AND", "s.service_id", $obj->access->getServicesString("ID", $obj->DBC)) . " GROUP BY sg_name,host_name,host_state,icon_image,host_id, s.state ORDER BY tri ASC "; - $DBRESULT = $obj->DBC->query($query2); + $dbResult = $obj->DBC->prepare($query2); + foreach ($queryValues2 as $bindId => $bindData) { + foreach ($bindData as $bindType => $bindValue) { + $dbResult->bindValue($bindId, $bindValue, $bindType); + } + } + $dbResult->execute(); $states = array( 0 => 'sk', @@ -215,7 +250,7 @@ ); $sg_list = array(); - while ($tab = $DBRESULT->fetchRow()) { + while ($tab = $dbResult->fetch()) { $sg_list[$tab["sg_name"]][$tab["host_name"]]['host_id'] = $tab['host_id']; $sg_list[$tab["sg_name"]][$tab["host_name"]]['icon_image'] = $tab['icon_image']; $sg_list[$tab["sg_name"]][$tab["host_name"]]['host_state'] = $tab['host_state'];