Николай Ланец
4 июня 2013 г., 19:55

Уязвимость в коннекторах MODX Revolution всех версий

Если коротко: все обновляйте свои сайты до последней версии (сейчас 2.2.8).
Я поленился обновляться, установил securitypatch20130604 в результате приходит сообщение с почты сайта «Нello!», которая нигде не указана, в журнале ошибок вижу: [2013-06-07 13:43:19] (ERROR @ /connectors/browser/file.php) Could not prepare context: msgr [2013-06-07 13:43:19] (ERROR @ /connectors/browser/file.php) Could not load context: mgr Получается патч не работает?
[2013-06-07 13:43:19] (ERROR @ /connectors/browser/file.php) Could not prepare context: msgr
Это как раз правильная ошибка. То есть кто-то пытался простучаться по этой уязвимости, но MODX не смог проинициализировать фейковый контекст, и записал ошибку в лог. Но при этом дальше выполнение коннектора/процессора должно быть бессмысленно, так как объект $modx->context после такой ошибочной инициализации просто отсутствует, и тот же вызов метода $modx->hasPermission() просто развалится фатальной ошибкой пыхпыха. Нет у меня уверенности, что это правильно (я предлагал вообще неудавшуюся попытку инициализации контекста разваливать ошибкой 500, как это происходит при неудавшейся инициализации самого MODX-а), но все равно поверхностно выглядит так, как будто дырку все-таки залатали. А письма приходят — скорее всего вы успели заразу поймать раньше. Затрите MODX новой версией прям поверх текущего сайта и проверьте имеющиеся сниппеты и плагины. Не лишним было бы переустановить все пакеты в системе.
То что заразу поймал раньше — исключено, просто у меня стоит пересылка любой почты с этого хостинга, видимо хацкер отправил письмо себе, чтобы проверить что у него все получилось, я как понял, сразу переименовал папку на хостинге, залил обновления и обновил все сайты. Сейчас все, проверил, вроде все нормально, обошлось.
Я на modxcloud.com все свои сайты держу. Во-первых, все сайты абсолютно отделены друг от друга (взломав один сайт, не залезешь ни в базу, ни в файлы другого). Во-вторых, обновление или переустановка MODX-а в два клика выполняется. В-третьих, оперируешь не полными копиями сайта, а снимками, которые не несут в себе ядро, а только пользовательские эддоны. В итоге ядро четко отделено от пользовательской части сайта, и если зараза и сидит где-то плотно, можно частями сайты перетащить на новый двиг, полностью проверив все составляющие. Vapor и packMan в помощь. А с modxSDK я вообще сейчас только в админке и программирую, так что вообще не парюсь с настройками доступов (FTP и т.п.).
Кстати, по поводу всех этих коннекторов и т.п. В своих пользовательских коннекторах я всегда явно указываю $_SERVER['ctx'] = 'web'; С этими коннекторами всегда вопрос безопасности стоял остро и первый мой багрепорт на эту тему был еще в 2011-ом.
Но это свои пользовательские коннекторы. А что делать с системными? Ведь во-первых, в ядро лезть — это совсем не по религии, а во-вторых, при переустановке MODX-а это все затрется. На этот счет есть очень элегантное решение: можно просто переопределить класс обработки процессоров. Вот в connectors/index.php есть такая строка:
$connectorRequestClass = $modx->getOption('modConnectorRequest.class',null,'modConnectorRequest');
А значит вы можете создать свой класс, расширить им базовый modConnectorRequest, прописать его в системную настройку с именем modConnectorRequest и в своем классе уже дополнительно прописать проверки.
К примеру в базовом классе есть метод initialize:
public function initialize() { if ($this->modx && is_object($this->modx->context) && $this->modx->context instanceof modContext) { $ctx = $this->modx->context->get('key'); if (!empty($ctx) && $ctx == 'mgr') { $ml = $this->modx->getOption('manager_language',null,$this->modx->getOption('cultureKey',null,'en')); if (!empty($ml)) { $this->modx->setOption('cultureKey',$ml); } } } /* load default core cache file of lexicon strings */ $this->modx->lexicon->load('core:default'); if ($this->modx->actionMap === null || !is_array($this->modx->actionMap)) { $this->loadActionMap(); } return true; }
Собственно, у меня есть пара вопросов к этому коду, но сейчас не об этом. В общем, переопределим этот метод и пропишем явно проверку контекста:
public function initialize() { if (!$this->modx OR !is_object($this->modx->context) OR !$this->modx->context instanceof modContext OR !in_array($this->modx->context->key, array('web', 'mgr'))){ return 'Access denied'; } return parent::initialize(); }
Все. Если контекст не будет инициализирован, или он не в списке разрешенных, то шлем лесом.

Добавить комментарий