Некоторое время назад в сканере уязвимостей веб-приложений Acunetix появилась возможность расширения функционала за счет добавления собственных модулей. На официальном блоге даже была опубликована заметка, описывающая общий подход к разработке модулей. Давайте напишем простой модуль на основе нее, а также моей прошлогодней заметки по созданию модуля для Nessus. С точки зрения функций модуль будет аналогичным: поиск на узле скрипта Adminer, старые версии которого позволяют читать произвольные файлы на сервере с помощью фичи MySQL LOAD DATA LOCAL INFILE.
Приступим. Исходя из вышеупомянутой статьи на официальном блоге, для создания модулей используется язык JavaScript. Модули необходимо размещать в следующих директориях:
- %PROGRAMDATA\Acunetix\shared\custom-scripts\ (Windows)
- /home/acunetix/.acunetix/data/custom-scripts/ (Linux)
Дальнейший путь зависит от типа модуля. Существует два типа модулей: запускаемые один раз для сканируемого хоста (их необходимо размещать в директории target), запускаемые на каждый HTTP-ответ при сканировании (директория httpdata). Нам доступны некоторые дополнительные интерфейсы, описание которых находится в файле native.d.ts (директория custom-scripts). Для нашего модуля мы воспользуемся вторым типом (на каждый HTTP-ответ), так как нам необходима информация о доступных путях, собранная Acunetix'ом, фактически, наш модуль будет запущен для каждого обнаруженного пути. Далее мы отфильтруем ситуации, когда наш модуль вызывается в контексте обращения к директории. Дополнительно активировать модуль не требуется, достаточно в профиле сканирования включить опцию Custom Scripts (на блоге Acunetix этот момент подробно описан). Перейдем к коду.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
let description = 'Adminer Database Management script was detected on the remote host. Affected versions (<4.6.3) may be susceptible to LOAD DATA LOCAL INFILE attack.'; // https://github.com/vrana/adminer // Версии вплоть до последней по состоянию на 26/06/2020 let version_list = ['','4.7.7','4.7.6','4.7.5','4.7.4','4.7.3','4.7.2','4.7.1','4.7.0','4.6.3','4.6.2','4.6.1','4.6.0','4.5.0','4.4.0','4.3.1','4.3.0','4.2.5','4.2.4','4.2.3','4.2.2','4.2.1','4.2.0','4.1.0','4.0.3','4.0.2','4.0.1','4.0.0','3.7.1','3.7.0','3.6.4','3.6.3','3.6.2','3.6.1','3.6.0','3.5.1','3.5.0','3.4.0','3.3.4','3.3.3','3.3.1','3.3.0','3.2.2','3.2.1','3.2.0','3.1.0','3.0.1','3.0.0']; // Регулярные выражения для извлечения версии и определения устаревших версий let re_version = RegExp('<span class="version">([0-9\\.]+)</span>', 'i'); let re_outdated = RegExp('^(?:[4]\.[6]\.[0-2]|[1-4]\.[0-5]\.[0-9]|[1-3]\.[0-9]\.[0-9])$', 'i'); // Дополнительные префиксы и суффиксы // можно взять из официального репозитория Adminer let prefix_list = ['adminer-', 'editor-', 'adminer']; let suffix_list = ['-en.php', '-mysql-en.php', '-mysql.php', '.php', '/']; let paths_list = []; let found_paths = []; |
Мы объявили переменную с описанием уязвимости, массивы для формирования возможных путей сценария Adminer, регулярные выражения для извлечения версии и проверки, является ли она устаревшей, и пару массивов для хранения возможных и обнаруженных путей. Первый массив необязателен, но немного упрощает восприятие кода и может быть использован для отладки. Накладные расходы с точки зрения используемой памяти несущественны.
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
// Нас интересуют только ответы, которые относятся к потенциальным директориям // Нет смысла срабатывать в ответ на обращение к каждому абстрактному пути if (scriptArg.location.isFolder) { // Создаем массив возможных путей Adminer for (const version of version_list) for (const prefix of prefix_list) for (const suffix of suffix_list) paths_list.push(prefix + version + suffix); // Проверяем каждый возможный путь for (const path of paths_list) { // Создаем объект для выполнения HTTP-запроса (интерфейс Acunetix'a) let job = ax.http.job(); // Настраиваем параметры // в соответствии с информацией о текущем сканируемом адресе job.hostname = scriptArg.target.host; job.port = scriptArg.target.port; job.secure = scriptArg.target.secure; job.request.uri = scriptArg.location.path + path; ax.http.execute(job).sync(); // Если все прошло успешно, // то пытаемся извлечь информацию о версии Adminer // И проверить, является ли она устаревшей // Результаты сохраняем в массив if (!job.error && job.response.status == 200) { let body = job.response.body; let match = body.match(re_version); if (match) { let version = match[1]; let entry = `[li]URL: ${path} (Version: ${version})`; if (re_outdated.test(version)) entry += ' - Affected!' entry += '[/li]'; found_paths.push(entry); } } } } // Выводим информацию об обнаруженных путях и потенциальной уязвимости if (found_paths.length > 0) { let details = description + '[break][break][ul]' + found_paths.join('') + '[/ul]'; scanState.addVuln({ // Доступные типы не документированы // Нельзя указать свое название для отображения в списке // Нельзя задать уровень критичности :-( typeId: 'custom.xml', location: scriptArg.location, details: details, }); } |
Стоит отметить, что описание доступных интерфейсов неполное как в блоге Acunetix, так и в файле native.d.ts, например, в коде я проверяю значение scriptArg.location.isFolder
, которое не описано. Так как это Javascript, то при разработке имеет смысл использовать отладочные сообщения (функция ax.log) для вывода всех доступных полей и методов объектов. Например, ниже приведены доступные свойства и методы scriptArg.location
, scriptArg.target
, scriptArg.http
с примерами значений (сканируемый хост - 192.168.0.1, порт 80):
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 |
location.parent -> "null" location.tags -> "[object Set]" location.target -> "[object Object]" location.sensorDetected -> "false" Location.schemeCount -> "0" location.hasInputs -> "false" location.isFolder -> "true" location.url -> "http://192.168.0.1/" location.name -> "" location.parentPath -> "/" location.path -> "/" location.hasTag -> "function hasTag() { [native code] }" location.inheritsTag -> "function inheritsTag() { [native code] }" location.hasChild -> "function hasChild() { [native code] }" target.root -> "[object Object]" target.caseInsensitive -> "false" target.server -> "[object Object]" target.ip -> "192.168.0.1" target.origin -> "http://192.168.0.1/" target.url -> "http://192.168.0.1/" target.secure -> "false" target.port -> "" target.host -> "192.168.0.1" http.sslInfo -> "null" http.response -> "HTTP/1.1 200 OK" |
Вообще, судя по отсутствию вменяемой документации и по доступным интерфейсам, возможность создавать собственные модули находится в начальной стадии. Разработка модулей для Nessus и OpenVAS гораздо лучше документирована, а доступные интерфейсы обладают бОльшими возможностями. Но что-то мы отвлеклись. Приведу примеры, как будут выглядеть результаты в веб-интерфейсе Acunetix после сканирования в случае, если что-то было обнаружено.
На этом на сегодня все. Ставьте лайк, подписывайтесь на канал, жмите колокольчик.
Модуль из статьи: adminer.js.zip
P.S. Когда-то на сайте Acunetix можно было свободно скачать демо-версию, а сейчас приходится просить знакомых, чтобы проверить работоспособность модуля. Неудобства на ровном месте. О существовании пиратских версий я знаю, но это не наш выбор.
Здравствуйте, помогите пожалуйста, понимаю, что тема заезжена.. Но, завтра серьезный экзамен, хотелось бы поготовиться. Использовал вашу программу MyTestXProHelper, не получилось, вытащить тест, пишет "can t find test data in memory" испробовал все, виртуальную машину XP итд. Все равно - "can t find test data in memory". Если у Вас получится вытащить тест, в долгу не останусь, обязательно отблагодарю вас, сколько скажете. Файл ПМ.01 экзешник.
В *.59 могут наблюдаться сложности с извлечением. Это возможно сделать вручную (на XP):
1. Взять x64dbg, включить Hide Debugger (или взять OllyDbg с настройками под VMProtect)
2. Открыть тест, поставить breakpoint на VirtualFree
3. Запустить, после срабатывания breakpoint просканировать память процесса в соответствии с сигнатурой из xml-файла в составе MyTestXProHelper
4. Установить hardware breakpoint on write для первого байта этого адреса
5. Перезапустить процесс (Ctrl+F2)
6. Первое срабатывание hardware breakpoint пропустить, а на втором по идее сигнатура в начале блока останется прежней, но содержимое будет расшифровано в памяти
7. Скопировать содержимое памяти от сигнатуры заголовка до сигнатуры конца (все из xml-файла) в *.mtx файл.
Пробовал аналогично из под Win XP. Но ничего не выходит (я рукожоп еще тот). У меня такая же ошибка "can't find test data in memory" при вытаскивании через MyTestXProHelper.
Тест приложен ниже
https://yadi.sk/d/nqubgfC5kkmpLA это архив с файлом