VYPR
Critical severityOSV Advisory· Published Apr 25, 2019· Updated Aug 5, 2024

CVE-2017-16558

CVE-2017-16558

Description

Contao 3.0.0 to 3.5.30 and 4.0.0 to 4.4.7 contains an SQL injection vulnerability in the back end as well as in the listing module.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
contao/contaoPackagist
>= 3.0.0, <= 3.5.30
contao/contaoPackagist
>= 4.0.0, < 4.4.84.4.8
contao/core-bundlePackagist
>= 4.0.0, < 4.4.84.4.8
contao/listing-bundlePackagist
>= 4.0.0, < 4.4.84.4.8
contao/core-bundlePackagist
>= 3.0.0, <= 3.5.30
contao/listing-bundlePackagist
>= 3.0.0, <= 3.5.30

Affected products

1

Patches

2
6b4a2711edf1

[Listing] Prevent SQL injections in the listing module (see CVE-2017-16558).

https://github.com/contao/contaoLeo FeyerNov 14, 2017via ghsa
2 files changed · +59 57
  • listing-bundle/CHANGELOG.md+4 0 modified
    @@ -1,5 +1,9 @@
     # Contao listing bundle change log
     
    +### DEV
    +
    + * Prevent SQL injections in the listing module (see CVE-2017-16558).
    +
     ### 4.4.0-RC1 (2017-05-23)
     
      * Show a "no results" notice if a search does not return any results (see contao/core#7705).
    
  • listing-bundle/src/Resources/contao/modules/ModuleListing.php+55 57 modified
    @@ -109,67 +109,64 @@ protected function compile()
     			return;
     		}
     
    -
    -		/**
    -		 * Add the search menu
    -		 */
    +		// Add the search menu
     		$strWhere = '';
     		$varKeyword = '';
     		$strOptions = '';
    +		$strSearch = \Input::get('search');
    +		$strFor = \Input::get('for');
    +		$arrFields = \StringUtil::trimsplit(',', $this->list_fields);
    +		$arrSearchFields = \StringUtil::trimsplit(',', $this->list_search);
     
     		$this->Template->searchable = false;
    -		$arrSearchFields = \StringUtil::trimsplit(',', $this->list_search);
     
     		if (!empty($arrSearchFields) && is_array($arrSearchFields))
     		{
     			$this->Template->searchable = true;
     
    -			if (\Input::get('search') && \Input::get('for'))
    +			if ($strSearch && !in_array($strSearch, $arrSearchFields, true))
    +			{
    +				$strSearch = '';
    +				$strFor = '';
    +			}
    +
    +			if ($strSearch && $strFor)
     			{
    -				$varKeyword = '%' . \Input::get('for') . '%';
    -				$strWhere = (!$this->list_where ? " WHERE " : " AND ") . \Input::get('search') . " LIKE ?";
    +				$varKeyword = '%' . $strFor . '%';
    +				$strWhere = (!$this->list_where ? " WHERE " : " AND ") . $strSearch . " LIKE ?";
     			}
     
     			foreach ($arrSearchFields as $field)
     			{
    -				$strOptions .= '  <option value="' . $field . '"' . (($field == \Input::get('search')) ? ' selected="selected"' : '') . '>' . (strlen($label = $GLOBALS['TL_DCA'][$this->list_table]['fields'][$field]['label'][0]) ? $label : $field) . '</option>' . "\n";
    +				$strOptions .= '  <option value="' . $field . '"' . (($field == $strSearch) ? ' selected="selected"' : '') . '>' . (strlen($label = $GLOBALS['TL_DCA'][$this->list_table]['fields'][$field]['label'][0]) ? $label : $field) . '</option>' . "\n";
     			}
     		}
     
     		$this->Template->search_fields = $strOptions;
     
    -
    -		/**
    -		 * Get the total number of records
    -		 */
    +		// Get the total number of records
     		$strQuery = "SELECT COUNT(*) AS count FROM " . $this->list_table;
     
     		if ($this->list_where)
     		{
     			$strQuery .= " WHERE (" . $this->list_where . ")";
     		}
     
    -		$strQuery .=  $strWhere;
    +		$strQuery .= $strWhere;
     		$objTotal = $this->Database->prepare($strQuery)->execute($varKeyword);
     
    -
    -		/**
    -		 * Validate the page count
    -		 */
    +		// Validate the page count
     		$id = 'page_l' . $this->id;
    -		$page = (\Input::get($id) !== null) ? \Input::get($id) : 1;
    -		$per_page = \Input::get('per_page') ?: $this->perPage;
    +		$page = (\Input::get($id) !== null) ? (int) \Input::get($id) : 1;
    +		$per_page = (int) \Input::get('per_page') ?: $this->perPage;
     
     		// Thanks to Hagen Klemp (see #4485)
     		if ($per_page > 0 && ($page < 1 || $page > max(ceil($objTotal->count/$per_page), 1)))
     		{
     			throw new PageNotFoundException('Page not found: ' . \Environment::get('uri'));
     		}
     
    -
    -		/**
    -		 * Get the selected records
    -		 */
    +		// Get the selected records
     		$strQuery = "SELECT " . $this->strPk . "," . $this->list_fields;
     
     		if ($this->list_info_where)
    @@ -192,16 +189,30 @@ protected function compile()
     			return $GLOBALS['TL_DCA'][$this->list_table]['fields'][$field]['eval']['rgxp'] == 'date' || $GLOBALS['TL_DCA'][$this->list_table]['fields'][$field]['eval']['rgxp'] == 'time' || $GLOBALS['TL_DCA'][$this->list_table]['fields'][$field]['eval']['rgxp'] == 'datim';
     		};
     
    +		$order_by = \Input::get('order_by');
    +
    +		if ($order_by && !in_array($order_by, $arrFields, true))
    +		{
    +			$order_by = '';
    +		}
    +
    +		$sort = \Input::get('sort');
    +
    +		if ($sort && !in_array($sort, array('asc', 'desc')))
    +		{
    +			$sort = '';
    +		}
    +
     		// Order by
    -		if (\Input::get('order_by'))
    +		if ($order_by)
     		{
    -			if ($isInt(\Input::get('order_by')))
    +			if ($isInt($order_by))
     			{
    -				$strQuery .= " ORDER BY CAST(" . \Input::get('order_by') . " AS SIGNED) " . \Input::get('sort');
    +				$strQuery .= " ORDER BY CAST(" . $order_by . " AS SIGNED) " . $sort;
     			}
     			else
     			{
    -				$strQuery .= " ORDER BY " . \Input::get('order_by') . ' ' . \Input::get('sort');
    +				$strQuery .= " ORDER BY " . $order_by . ' ' . $sort;
     			}
     		}
     		elseif ($this->list_sort)
    @@ -219,9 +230,9 @@ protected function compile()
     		$objDataStmt = $this->Database->prepare($strQuery);
     
     		// Limit
    -		if (\Input::get('per_page'))
    +		if ($per_page)
     		{
    -			$objDataStmt->limit(\Input::get('per_page'), (($page - 1) * $per_page));
    +			$objDataStmt->limit($per_page, (($page - 1) * $per_page));
     		}
     		elseif ($this->perPage)
     		{
    @@ -230,10 +241,7 @@ protected function compile()
     
     		$objData = $objDataStmt->execute($varKeyword);
     
    -
    -		/**
    -		 * Prepare the URL
    -		 */
    +		// Prepare the URL
     		$strUrl = preg_replace('/\?.*$/', '', \Environment::get('request'));
     		$blnQuery = false;
     
    @@ -249,13 +257,9 @@ protected function compile()
     		$this->Template->url = $strUrl;
     		$strVarConnector = $blnQuery ? '&amp;' : '?';
     
    -
    -		/**
    -		 * Prepare the data arrays
    -		 */
    +		// Prepare the data arrays
     		$arrTh = array();
     		$arrTd = array();
    -		$arrFields = \StringUtil::trimsplit(',', $this->list_fields);
     
     		// THEAD
     		for ($i=0, $c=count($arrFields); $i<$c; $i++)
    @@ -271,10 +275,10 @@ protected function compile()
     			$strField = strlen($label = $GLOBALS['TL_DCA'][$this->list_table]['fields'][$arrFields[$i]]['label'][0]) ? $label : $arrFields[$i];
     
     			// Add a CSS class to the order_by column
    -			if (\Input::get('order_by') == $arrFields[$i])
    +			if ($order_by == $arrFields[$i])
     			{
    -				$sort = (\Input::get('sort') == 'asc') ? 'desc' : 'asc';
    -				$class = ' sorted ' . \Input::get('sort');
    +				$sort = ($sort == 'asc') ? 'desc' : 'asc';
    +				$class = ' sorted ' . $sort;
     			}
     
     			$arrTh[] = array
    @@ -319,7 +323,7 @@ protected function compile()
     				$arrTd[$class][$k] = array
     				(
     					'raw' => $v,
    -					'content' => ($value ? $value : '&nbsp;'),
    +					'content' => $value ?: '&nbsp;',
     					'class' => 'col_' . $j . (($j++ == 0) ? ' col_first' : '') . ($this->list_info ? '' : (($j >= (count($arrRows[$i]) - 1)) ? ' col_last' : '')),
     					'id' => $arrRows[$i][$this->strPk],
     					'field' => $k,
    @@ -332,31 +336,25 @@ protected function compile()
     		$this->Template->thead = $arrTh;
     		$this->Template->tbody = $arrTd;
     
    -
    -		/**
    -		 * Pagination
    -		 */
    +		// Pagination
     		$objPagination = new \Pagination($objTotal->count, $per_page, \Config::get('maxPaginationLinks'), $id);
     		$this->Template->pagination = $objPagination->generate("\n  ");
     		$this->Template->per_page = $per_page;
     		$this->Template->total = $objTotal->count;
     
    -
    -		/**
    -		 * Template variables
    -		 */
    +		// Template variables
     		$this->Template->action = \Environment::get('indexFreeRequest');
    -		$this->Template->details = ($this->list_info != '') ? true : false;
    +		$this->Template->details = (bool) $this->list_info;
     		$this->Template->search_label = \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['search']);
     		$this->Template->per_page_label = \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['list_perPage']);
     		$this->Template->fields_label = $GLOBALS['TL_LANG']['MSC']['all_fields'][0];
     		$this->Template->keywords_label = $GLOBALS['TL_LANG']['MSC']['keywords'];
    -		$this->Template->search = \Input::get('search');
    -		$this->Template->for = \Input::get('for');
    -		$this->Template->order_by = \Input::get('order_by');
    -		$this->Template->sort = \Input::get('sort');
    +		$this->Template->search = $strSearch;
    +		$this->Template->for = $strFor;
    +		$this->Template->order_by = $order_by;
    +		$this->Template->sort = $sort;
     		$this->Template->col_last = 'col_' . $j;
    -		$this->Template->no_results = sprintf($GLOBALS['TL_LANG']['MSC']['sNoResult'], \Input::get('for'));
    +		$this->Template->no_results = sprintf($GLOBALS['TL_LANG']['MSC']['sNoResult'], $strFor);
     	}
     
     
    
501cb3cd34d6

[Core] Prevent SQL injections in the back end search panel (see CVE-2017-16558).

https://github.com/contao/contaoLeo FeyerNov 14, 2017via ghsa
2 files changed · +9 2
  • core-bundle/CHANGELOG.md+1 0 modified
    @@ -2,6 +2,7 @@
     
     ### DEV
     
    + * Prevent SQL injections in the back end search panel (see CVE-2017-16558).
      * Support class named services in System::import() and System::importStatic() (see #1176).
      * Only show pretty error screens on Contao routes (see #1149).
     
    
  • core-bundle/src/Resources/contao/drivers/DC_Table.php+8 2 modified
    @@ -5068,8 +5068,14 @@ protected function searchMenu()
     			$strField = \Input::post('tl_field', true);
     			$strKeyword = ltrim(\Input::postRaw('tl_value'), '*');
     
    +			if ($strField && !in_array($strField, $searchFields, true))
    +			{
    +				$strField = '';
    +				$strKeyword = '';
    +			}
    +
     			// Make sure the regular expression is valid
    -			if ($strKeyword != '')
    +			if ($strField && $strKeyword)
     			{
     				try
     				{
    @@ -5187,7 +5193,7 @@ protected function sortMenu()
     			$strSort = \Input::post('tl_sort');
     
     			// Validate the user input (thanks to aulmn) (see #4971)
    -			if (in_array($strSort, $sortingFields))
    +			if (in_array($strSort, $sortingFields, true))
     			{
     				$session['sorting'][$this->strTable] = in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$strSort]['flag'], array(2, 4, 6, 8, 10, 12)) ? "$strSort DESC" : $strSort;
     				$objSessionBag->replace($session);
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.