
Статический аудит кода CVE-2024-57521 — аутентифицированной SQL-инъекции в модуле генератора RuoYi-Vue, с анализом от источника к стоку и PoC обхода фильтра через %0b.
Рис. 1: Схема аудита потока данных уязвимости SQL-инъекции CVE-2024-57521
Данный репозиторий предназначен исключительно для целей исследования безопасности и обучения. Не используйте описанные в этом документе методы для несанкционированных атак. Все тесты выполнялись в локальной среде статического аудита кода.
ruoyi-generator)ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.javaКлючевой код:
@PostMapping("/createTable")
public AjaxResult createTableSave(@RequestParam("sql") String sql, @RequestParam("tplWebType") String tplWebType)
{
try
{
SqlUtil.filterKeyword(sql); // 在进入业务前调用过滤器
Анализ: администратор бэкенда может через параметр sql HTTP POST-запроса передать произвольную инструкцию создания таблицы базы данных. Точка входа не имеет достаточных ограничений пользовательского ввода и напрямую передаёт его последующему фильтру.
Скриншот-доказательство:

· Расположение файла: ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java
Код с критическим дефектом:
// 第16行:黑名单定义,注意关键词带有【尾随空格】
public static String SQL_REGEX = "\u0008|%0A|and |extractvalue|updatexml|sleep|information_schema|exec...";
// 第61-66行:过滤逻辑
String normalizedValue = value.replaceAll("\\p{Z}|\\s", ""); // 先清空了输入里的所有空格
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
for (String sqlKeyword : sqlKeywords)
{
if (StringUtils.indexOfIgnoreCase(normalizedValue, sqlKeyword) > -1)
{
throw new UtilException("请求参数包含敏感关键词" + sqlKeyword + ",可能存在安全风险");
}
}
Анализ уязвимости: фильтр сначала выполняет replaceAll("\p{Z}|\s", ""), удаляя все пробельные символы из входных данных. Однако ключевые слова в чёрном списке SQL_REGEX (такие как "and ", "select ") содержат завершающий пробел. Это приводит к тому, что сопоставление неизбежно терпит неудачу: как только злоумышленник использует %0b (вертикальный табулятор) вместо пробела после ключевого слова, он может идеально обойти проверку чёрного списка.
Доказательства на скриншотах:
· Расположение файла: ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml
Ключевой код:
<update id="createTable">
${sql}
</update>
Анализ: в MyBatis ${} напрямую конкатенирует строки, а не использует безопасную предварительную компиляцию #{}. В результате вредоносный SQL, уже обошедший фильтр, отправляется в базу данных на выполнение. Это типичная комбинированная уязвимость «фильтрация по чёрному списку + небезопасная конкатенация».
Скриншот-доказательство:
· Метод: злоумышленник использует %0b (вертикальная табуляция MySQL, входящая в диапазон совпадения \s) вместо пробелов в SQL-выражении.
· Принцип обнаружения: использование IF(<условие>, 1, 1/0) в качестве детектора булевой слепой инъекции. · Условие ложно: срабатывает ошибка деления на ноль, сервер возвращает HTTP 500. · Условие истинно: ошибки нет. · Способ извлечения: с помощью метода бисекции можно побитово угадывать данные базы данных.
Эта уязвимость — типичный обход, вызванный «неполным исправлением». При исправлении предыдущей аналогичной SQL-инъекции разработчик ввёл механизм чёрного списка, но упустил, что чёрный список зависит от пробелов, а предварительная логика удаления пробелов нарушает условия совпадения чёрного списка, что в итоге приводит к новому обходу (CVE-2024-57521).
· Отказаться от механизма чёрного списка: не полагайтесь на чёрный список, безопасной основой является механизм белого списка.
· Использовать предварительную компиляцию: замените ${sql} в MyBatis на предварительную компиляцию #{}. Если необходимо динамически передавать SQL (например, создание таблиц, Order By), следует использовать строгий разбор абстрактного синтаксического дерева (AST) или строгую проверку параметров.
· Усовершенствовать фильтр: если необходимо использовать чёрный список, нужно удалить завершающие пробелы у ключевых слов чёрного списка и перед сравнением выполнять единую нормализацию (например, приводить к нижнему регистру, заменять %0b и т. п. на пробелы).
· NVD - CVE-2024-57521 · Репозиторий RuoYi-Vue на Gitee
poc/poc_boolean.py (примечание: рекомендуется в дальнейшем переименовать файл в poc_error_based.py, чтобы он соответствовал фактической логике)extractvalue() для вызова ошибки XPath, извлечение данных через регулярное совпадение XPATH syntax error: '~...~'. В Payload используется /**/ вместо пробелов для обхода фильтрации по чёрному списку.
CREATE//table//{随机表名}//as//SELECT/**/extractvalue(1,concat(0x7e,({查询语句}),0x7e))
Примечание к тестированию: в локально развёрнутом тестовом стенде RuoYi успешно подтверждена возможность обхода WAF с помощью %0b, а также работоспособность error-based инъекции через extractvalue; скрипт стабильно извлекает версию MySQL (5.7.26), имя текущей базы данных (ry) и пользователя подключения к базе данных (root@localhost).