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.
| Package | Affected versions | Patched versions |
|---|---|---|
contao/contaoPackagist | >= 3.0.0, <= 3.5.30 | — |
contao/contaoPackagist | >= 4.0.0, < 4.4.8 | 4.4.8 |
contao/core-bundlePackagist | >= 4.0.0, < 4.4.8 | 4.4.8 |
contao/listing-bundlePackagist | >= 4.0.0, < 4.4.8 | 4.4.8 |
contao/core-bundlePackagist | >= 3.0.0, <= 3.5.30 | — |
contao/listing-bundlePackagist | >= 3.0.0, <= 3.5.30 | — |
Affected products
1Patches
26b4a2711edf1[Listing] Prevent SQL injections in the listing module (see CVE-2017-16558).
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 ? '&' : '?'; - - /** - * 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 : ' '), + 'content' => $value ?: ' ', '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).
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- github.com/advisories/GHSA-w38g-hj45-mjjpghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2017-16558ghsaADVISORY
- contao.org/de/changelog/versions/4.4.htmlghsax_refsource_CONFIRMWEB
- contao.org/en/news/contao-4_4_8.htmlghsax_refsource_CONFIRMWEB
- github.com/FriendsOfPHP/security-advisories/blob/master/contao/contao/CVE-2017-16558.yamlghsaWEB
- github.com/FriendsOfPHP/security-advisories/blob/master/contao/core-bundle/CVE-2017-16558.yamlghsaWEB
- github.com/FriendsOfPHP/security-advisories/blob/master/contao/listing-bundle/CVE-2017-16558.yamlghsaWEB
- github.com/contao/contao/blob/4.4.57/CHANGELOG.mdghsaWEB
- github.com/contao/contao/commit/501cb3cd34d61089b94e7ed78da53977bc71fc3eghsaWEB
- github.com/contao/contao/commit/6b4a2711edf166c85cfd7a53fed6aea56d4f0544ghsaWEB
News mentions
0No linked articles in our index yet.