D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Автор petrinh1988
Источник https://xss.is
Важное замечание: в начальной части статьи дана базовая информация. Если она не интересна, крути вниз до заголовка Custom Scripts, мясо там.
Базовая информация по сканированию в Acunetix
Для начала, давайте разберемся, как вообще работает поиск уязвимостей в Acunetix. Начнем с самого простого - интерфейса пользователя.
Чтобы создать сканирование, у нас есть два пути:
Тонкая настройка целей сканирования, при кажущейся простоте, тянет на отдельную статью. Если кому-то будет интересно, обязательно сделаю подробный разбор. Мы же перейдем к профилям сканирования, ведь они определяют какие именно чекеры будут запущены.
В базовой конфигурации, доступны одиннадцать профилей:
Спойлер: Из справки Acunetix
Full Scan: Использует профиль полного сканирования для запуска сканирования с использованием всех проверок, доступных в Acunetix. Этот профиль обеспечивает наиболее полный охват уязвимостей.
Critical / High Risk: Проверяет только самые опасные веб-уязвимости, такие как межсайтовый скриптинг, SQL-инъекция, включение файлов и т. д. Проверки, включенные в профиль сканирования критических/высоких рисков, динамически генерируются с каждым выпуском для добавления последних критических проверок и проверок высокой степени серьезности.
Critical / High / Medium Risk: Проверяет только веб-уязвимости, которые подвергают сайт критическому и высокому риску взлома, а также неправильные настройки сервера среднего риска и ошибки кодирования сайта. Проверки, включенные в критические, высокие и средние риски, динамически генерируются с каждым выпуском для добавления последних проверок критического, высокого и среднего уровня серьезности.
Cross-site Scripting: Проверяет только уязвимости межсайтового скриптинга. Этот профиль сканирования создается динамически, то есть он обновляется с каждой версией и включает в себя последние проверки.
SQL Injection: Проверяет только уязвимости SQL-инъекций. Этот профиль сканирования создается динамически, то есть он обновляется с каждой версией и включает в себя последние проверки.
Weak Passwords: Этот профиль сканирования определяет формы, которые принимают имя пользователя и пароль. Он будет атаковать эти формы для выявления уязвимостей.
Crawl Only: Только сканирует сайт и строит его структуру без каких-либо проверок на уязвимости.
OWASP Top 10: OWASP Top 10: проверяет только наиболее критические угрозы безопасности веб-приложений, определенные OWASP Top 10.
PCI checks: Проверки PCI: выявляют уязвимости, которые не соответствуют стандартам безопасности данных индустрии платежных карт (PCI).
Sans Top 25: Проверяет только 25 самых опасных ошибок программного обеспечения из списка Common Weakness Enumeration (CWE).
Malware Scan Про это в справке чуть-чуть забыли... но вот что удалось найти на просторах интернет:
Acunetix выполняет сканирование файлов на наличие вредоносных программ, сканируемых Acunetix. Сканирование вредоносных программ Acunetix разработано специально для обнаружения вредоносного ПО JavaScript, которое могло быть внедрено в ваше веб-приложение. Это может произойти при использовании сторонних библиотек, которые могли быть уже заражены, или в результате предыдущего взлома (в ходе которого злоумышленники внедрили вредоносное ПО). Сканирование на наличие вредоносных программ выполняется автоматически при полном сканировании, сканировании уязвимостей с высоким риском или сканировании на наличие вредоносных программ.При установке в Windows Acunetix автоматически использует Защитник Windows. Это программное обеспечение уже установлено вместе с Windows. Никакой дополнительной настройки не требуется.При установке в Linux или MacOS Acunetix использует ClamAV. Вам необходимо установить ClamAV отдельно, и Acunetix автоматически будет использовать его для сканирования на наличие вредоносных программ. В более старых версиях Windows, в которых нет вредоносного компонента Защитника Windows, вы также можете использовать ClamAV.
Черный ящик, к сожалению, пока остается черным ящиком. Меня беспокоит вопрос того, как создаются скрипты, но похоже они загружаются из подкапотной части Acunetix.Беспокоит по причине того, что кастомные скрипты сваливаются в кучу и запускаются тоже кучей. Либо все, либо никто. Если надо какой-то скрипт отключить, придется лезть в его код и «консервировать», а хотелось бы так же выбирать галочкой. Может в будущих версиях поправят, но в 24.2.240226074 все так.
Перейду к созданию своих профилей сканирования. По началу, хватало «Critical/Hight Risk» и «SQL Injection», но разбираясь со специфическими уязвимостями, пришел к выводу, что это не эффективно и нужны более тонко настроенные. Например, если напарсил Wordpress-сайтов, логичным будет создать отдельный профиль сканирования. Для этого иду в Scan Profiles (слева в меню) и жму Add New Profile
Вбиваю название нового профиля «Wordpress Scaner» и в поиске «wordpress», чтобы увидеть все доступные чекеры:
Зачем это нужно? Чтобы сократить время каждого отдельного сканирования и снизить нагрузку на рабочую машину. Если интересен конкретный набор уязвимостей и работаем на количество, зачем затягивать процесс? Проще потом повторно просканить по другим профилям.
Процесс сканирования
Процесс любого сканирования окунем проходит в три этапа:
Так выглядит результат работы кастомного чекера, который мы скоро напишем. Почти 35 минут и 8000 запросов, при том, что сам чекер делает всего 7 запросов… И чем больше сайт, тем больше времени уйдет на скан.
Для примера, натравил скрипт на пустой index.html, вот какие скрипты он запустил. Синим подчеркнут кастомный скрипт, ради которого и делался запуск.Помимо него, Acunetix выполнил девять чеков. Причем, на время повлияет не только количество страниц и ссылок на сайте, но и используемые технологии.
Поэтому, важно понимать, что чекер может быть лишь добавлением к другим. Но дополнением полезным, так как случайная нелепая уязвимость может стать серьезной точкой опоры.Поэтому, почему бы не повесить тематические чеки на сканы?
Но, вернемся к сканированию. Первым делом, Acunetix имитирует поведение обычного пользователя сайта, чтобы собрать первичную поверхностную информацию: ходит по страницам, прокручивает, тыкает мышкой и т.д.. Для этого используется связка Chromium + Puppeteer. Именно 20 загруженных инстансов Chromium, которые плотно эксплуатируются через Puppeteer, и жрут память и процессорное время.
После этапа сбора информации, окунь анализирует полученные данные. На данном этапе происходят определенные чеки уязвимостей в полученных данных и html-структуре, а так же выстраивается структура предстоящих тестов для третьего этапа. Если говорить о программной части процесса, выполняются скрипты из папки «httpdata», скоро эта информация потребуется.
Этап тестирования. Здесь окунь многократно пытается выполнить вшитые в него чеки, на основе результатов анализа. Если есть куда запихать SQLi и предполагается подобная проверка, будет долбить разными видами инъекций. Нет параметров куда пихать инъекцию или не требуются подобные проверки? Идет на другие чеки. В целом, думаю смысл понятен.
Custom Scripts
В процессе работы с Acunetix, пришло понимание необходимости писать свои скрипты. К счастью, есть абсолютно законный путь. Как пишут в справке окуня, чекеры можно писать на JavaScript, TypeScript или ECMAScript (а есть разница с JS?))) ).
Достаточно написать несложный скрипт, используя предоставляемые Acunetix объекты и положить в одну из папок:
Вернее, это корневые папки для кастомных скриптов, а в этих папках есть подпапки: httpdata и target. Это ключевой момент. В зависимости от того, в какую подпапку положить скрипт, зависит какой тип проверки будет использоваться и на каком этапе, а так же будут доступны разные возможности.
Для примера, в каждой папке лежит по скрипту demo.js, которые неплохо прокомментированы. В них приведены базовые возможности. Для запуска, достаточно высвободить скрипт из блокирующих if (false) и удалив /// disabled: true, создать профиль сканирования включающий Custom scripts и запустить этим профилем скан любого сайта.
Как и писал выше, httpdata относится к анализу информации, полученной в ходе этапа Discoverу. Кастомный скрипт будет применяться ко всем запросам предыдущего этапа. Таким образом, удастся прочекать каждую полученную страницу сайта, json, xml и всему что принимает окунь.
Скрипты из папки target используются для взаимодействия с целью или запросов к другим сервисам. Отсюда будут посылаться запросы, которые в Acunetix реализуются через ax.http.job(). Этот тип скриптов, в отличии от httpdata, запускается только один раз, а не на каждый ответ полученный Acunetix.
Кстати, раз уж затронул, в контексте выполнения скриптов доступны интерфейсы Acunetix: ax, scriptArg и scanState. ax, дает все необходимые методы для взаимодействия с целью. scriptArg позволяет получить аргументы выполнения скрипта и методы связанные с ним. Последний, понятное дело, дает связь с состоянием сканирования. В том числе, добавлением обнаруженных уязвимостей.
Подробнее про ax, scriptArg и scanState
Видимо философия Acunetix предполагает, что пользоваться ПО могут только технически подкованные люди с талантом исследователей. Других причин плохого описания, как API, так и доступных объектов при написании кастомных чекеров, я не вижу.
Часть информации можно почерпнуть из файла native.d.ts, который лежит в папке custom-scripts. Там можно почерпнуть информацию об ax. Файл есть на всех компьютерах, где установлен окунь. Некоторая информация присутствует в справке: https://www.acunetix.com/blog/docs/adding-custom-vulnerabilities/ и все… остальное можно добыть только опытным путем.
Предлагаю посмотреть на интерфейсы, чтобы в целом представлять возможности разработчиков. Для этого, исользую функцию логирования и распечатаю объекты. Вот простой код, который я поместил в папку custom-scripts/target, файл print.js
JavaScript: Скопировать в буфер обмена
Да, это полноценный рабочий код. В скрипт можно поместить одну строчку, которая отправляет данные в лог и этого будет достаточно. Можно сделать скрипт, который будет просто создавать уязвимость, каждый раз, когда его запускает окунь...
Вот так выглядит объект scriptArg:
Спойлер: sciptArg
JavaScript: Скопировать в буфер обмена
Большинство свойств понятны без комментариев. По сути, у нас есть location, target и http описывающие контекст взаимодействия с целью. Для примера будут использованы только: scriptArg.location для указания точки уязвимости, а так же два свойства объекта scriptArg.target. Из последнего, мы возьмем хост для настройки запроса к ресурсу и флаг secure, который указывает на необходимость использовать SSL/TLS.
Аналогичным образом получаю scanState и ax:
Спойлер: scanState
check = '[object Object]' typeof object"
check.name = '/custom-scripts/target/check_zips.js' typeof string"
check.firstTime = 'false' typeof boolean"
addVuln = 'function addVuln() { [native code] }' typeof function"
hasVuln = 'function hasVuln() { [native code] }' typeof function"
addLink = 'function addLink() { [native code] }' typeof function"
addExternalLink = 'function addExternalLink() { [native code] }' typeof function"
addHttpRequest = 'function addHttpRequest() { [native code] }' typeof function"
hintLinks = 'function hintLinks() { [native code] }' typeof function"
addHttp = 'function addHttp() { [native code] }' typeof function"
getGlobal = 'function getGlobal() { [native code] }' typeof function"
setGlobal = 'function setGlobal() { [native code] }' typeof function"
findUrl = 'function findUrl() { [native code] }' typeof function"
findPath = 'function findPath() { [native code] }' typeof function"
setTags = 'function setTags() { [native code] }' typeof function"
saveTest = 'function saveTest() { [native code] }' typeof function"
getAtomicCounter = 'function getAtomicCounter() { [native code] }' typeof function"
checkPresence = 'function checkPresence() { [native code] }' typeof function"
addTech = 'function addTech() { [native code] }' typeof function"
Из всех указанных свойств и методов, есть описание исключительно по addVuln (добавление уязвимости и его буду использоватьв в коде), а так же hintLinks, позволяющий передать acunetix массив ссылок о которых иначе он не сможет узнать. Например, скрытые файлы. По всему остальному можно толкьо догадываться. Например, понятное дело, что hasVuln нужен для проверки, может подобная уязвимость уже есть. но что именно принимает - не очень понятно. Или get и setGlobal. Судя по всему, позволяют скриптам взаимодействовать через глобальные параметры... В целом, эта статья и не имеет цели исследовать все возможности каждого интерфейса. В данном случае, это больше справочная информация, чтобы подстегнуть полет мысли.
Самый важный и большой ax:
Спойлер: ax
JavaScript: Скопировать в буфер обмена
Собственно, насколько подробная документация у Acunetix, видно невооруженным взглядом. Возможно, когда-нибудь, разберу все доступные методы и свойства. Сейчас же потребуется некий минимум, достаточный для первого полезного чекера.
Логгирование
Как вы поняли, из кода выше, для логирования используется функция ax.log(), которая принимает два аргумента: LogLevel и текст сообщения. Например:
ax.log(ax.LogLevelWarning, 'JOB END')
вернет
2024-05-31T13:21:24.711983 WARN /custom-scripts/target/check_zips.js 18236 JOB END
LogLevel влияет только на значение, которое в логе идет после даты и времени. Нигде в интерфейсе или еще где-то, никаких отличий не увидел. Видимо, это важно при чтении логов через какое-то ПО, которым я не пользуюсь.
Путь к файлу лога можно найти во вкладке Events в карточке сканирования
Внутри архива четыре файла, но принципиально нужный это logfile.csv. Для ускорения поиска своих комментов, открываю файл в Excel и пользую фильтры. Иначе черт ногу сломит, так как запуски сценариев выполняются асинхронно. Даже при том, что свои запросы запускаю синхронно, между ними налетает толпа респонсов от других чеков.
Кстати, по умолчанию, окунь хранит логи за два года... Настройка срока хранения находится в Settings (меню слева)
Чекер забытых админами zip-архивов
Идею подсмотрел здесь же, на форуме. Смысл уязвимости в том, что админы, порой, забывают удалить архивы. В них может быть очень любопытная информация, иногда критическая. Мне попадались полные бэкапы сайтов, включая логины и пароли от баз данных, файлы конфигурации.
Из информации на форуме, а также своего опыта, есть смысл искать следующие файлы: admin.zip, domaincom.zip (с точками без точек и т.д.), dev.zip, site. Понятное дело, что вариаций гораздо больше, но для примера будет достаточно этого, а расширить словарик можно в любой момент.
Сейчас код будет кусками и с отвлечениями на пояснения тех или иных важных моментов, дальше выложу целиком.
Создаю в папке target файл check_zip.js и объявляю нужные константы:
JavaScript: Скопировать в буфер обмена
Код берет три жестко заданных названия архивов, а так же от двух до четырех вариаций названий с использованием хоста. Если на вход придет «www.site.com», на выходе будет такой массив:
JavaScript: Скопировать в буфер обмена
Остальные константы, соответственно названиям и значениями. Названия найденных архивов складываю в result, чтобы потом вывести одной уязвимостью все.Конечно, вряд ли бывает, чтобы архивов было много, но решил пиать так.
goodStatuses для чека статуса… насто натыкался на 301-302, которые ведут на какую-то страницу, которую админ сайта решил показывать вместо ошибочных урлов. Бывает еще 403я ошибка, которую выдает WAF, когда видит запрос zip-файла.Это все ненужное и отвлекает.
Но и 200 не гарантирует, что нашелся архив, поэтому что хитросделанные сеошники могут требовать отдавать морду сайта вместо 404. Поэтому проверяю еще и content-type, в обязательном порядке..
Вся последующая работа будет проходить в цикле, который обходит вариации имен файлов:
JavaScript: Скопировать в буфер обмена
Запросы к цели выполняются через ax.http.job(), который нужно настроить соответствующим образом. Добавить необходимые заголовки, указать используется ли https, указать конкретный путь на сайте и адрес хоста. Да, адрес хоста указывается отдельно, т.к. job не привязывается к таргету и возможны запросы к совершенно другим хостам.
JavaScript: Скопировать в буфер обмена
В данном случае, адрес хоста и secure берется из scriptArgs. Путь, соответственно, это имя архива в корне сайта, например, ‘/admin.zip’. retries - количество попыток. Можно также добавить заголовок Range, чтобы ограничить количество возвращаемых байт. Архивы бывают достаточно большими, поэтому ограничил 10ю килобайтами. Хотя признаюсь, что добавил заголовок больше для демонстрации.
Запрос готов, остается только запустить запрос в работу методом ax.http.execute в синхронном режиме.
JavaScript: Скопировать в буфер обмена
Асинхронно я не пытался запускать, хотя вроде бы ничего не мешает через тот же then прикрутить функцию обрабатывающую результат.
Остается только проверить, успешно ли был выполнен запрос и какой статус ответа. Кроме 200 или 206, ничего не жду, т.к. ищу прямую ссылку на скачивание.
JavaScript: Скопировать в буфер обмена
Важный момент, это не путать свойства, которые относятся к самому ax.http.job, какие к ax.http.job.request, а какие к ответу ax.http.job.response. С непривычки, убил час на то, чтобы понять почему скрипт работал неправильно. Как обычно бывает с ошибкой на уровне запятой, уже бился головой об стену и думал, что может проблема в нуленной версии…. оказалось, что неверно проверял status. Пытался получить его из job, хотя надо было проверять job.response.status.
Ниже выдержка из справки по этим объектам. Правда, как показывает практика работы с Acunetix, лучше бы их распечатать и посмотреть, что еще присутствует из недокументированного. Но это не тема нашей сегодняшней телепередачи, иначе просмотр затянется и так вышло не мало.
Спойлер: ax.http.job
hostname: Mandatory. The host or domain name of the server to connect to. Example: www.example.com
secure: Optional. Whether or not the connection should use HTTPS and be secured by TLS/SSL. Can be set to either true or false, with the default being false.
port: Optional. The TCP destination port on which to connect. This setting is optional, and its default value is 80, unless secure is set to true, in which case port will default to 443. The port must be set as a string: job.port = “8080”;
timeout: Optional. The number of milliseconds after which a pending, incomplete connection attempt should be considered as unsuccessful, and cancelled. Must be passed as numeric value. Default: 30000. Example: job.timeout = 10000;
retries: Optional. The number of times a request which has timed out should be retransmitted. Must be passed as numeric value. Default: 3. Example: job.retries = 1;
Спойлер: ax.http.job.request
uri: The URI to request. Default: /. Example: job.request.uri = ‘/path/’;’
method: Usually ‘GET’ or ‘POST’, but arbitrary values are supported. The default value is ‘GET’.
body: The request body to send.
addHeader(name: string, value: string): Allows for the insertion of custom request headers. Example: job.request.addHeader(‘X-Header’, ‘Value’)
Спойлер: ax.http.job.response
version: The HTTP version the server responded with. Example: job.response.version might return the string “HTTP/1.1”
status: The numeric HTTP status code. Common status codes are 200, 301, and 404. An explanation of status codes can be found at https://developer.mozilla.org/en-US/docs/Web/HTTP/Status.
reason: A string representation of the status code. Example: OK
headers: The ax.http.Job.Response.Headers interface gives you convenient access to the received response headers. If a simple text representation of all response headers is preferable, the toString() function can be used: job.response.headers.toString(). To process individual headers, has(name: string): boolean will return information about whether a response header with the given name exists. If so, get(name: string): string may be used to retrieve its value. Example: if(job.response.headers.has(‘Content-Type’)) ax.log(ax.LogLevelInfo, job.response.headers.get(‘content-type’)) – note that header names are not case-sensitive
body: The response body
Что же, запрос скрипт делает, ответ проверяет и складывает в массив. Причем, в двух возможных вариантах. Если контент тайп верный, скрипт уверенно заявляет о правильном ответе. Если нет, скрипт пишет, что возможно есть архив. Хотя это не совсем правильно...
И так, то ради чего все затевалось - добавление вулнерабилити в Acunetix.
JavaScript: Скопировать в буфер обмена
typeid, по всей видимости, используется для совместимости с движком окуня. По идее, можно подпихнуть здесь другой вулнерабилити, но высока вероятность, что у пациента окончательно поедет крыша и все сломается. Поэтому не мучаем, оставляем custom.xml. Как бы не было смешно, параметр обязательный и всегда один и тот же… Кстати, кастом уязвимости всегда выводится как High Risk с вероятностью 95%.
Хотя, конечно же, верить справке окуня, себя не уважать… но попытки добавить в объект параметр confidence, результато не дали. Пробовал и 100, и 1 и даже строковый 100%. Но, возможно, просто по другому параметр называть надо.
location или path — должен быть указан хотя бы один из этих параметров. Они взаимозаменяемые и взаимодополняющие друг друга. Разница в том, что location должен быть объекмто ax.state.Location. Например, туда втыкают scriptArg.location. path — обычная строка. Например, слэш ('/').
http - очень крутой и очень важный параметр. Благодаря ему в карточках уязвимостей отображаются детальные HTTP Response и HTTP Request. В этом примере параметр опущен, так как является необязательным и абсолютно не имеет смысла для рассматриваемой ситуации. Но если важно видеть запрос и ответ, добавляем в объект уязвимости. Добавление выглядит так:
JavaScript: Скопировать в буфер обмена
Т.е. просто передаем отработавший Job и не паримся. Кстати, если надо использовать параметр, то не получится повесить кучу уязвимостей, как в нашем примере. Либо на каждый сработавший запрос создавать по одной Vulnerability. Либо, другой вариант, сохранять один job, создавать для него одну уязвимость и пихать в нее остальные ответы списком. Как, например, это сделано при обнаружении Time-based или Boolean-based SQLi
Синим выделил все запросы, которые сканер выдал просто списком с результатами. Красным пример ответа, составленный из Job.
Кстати, вот это красивое выделение жирным и вывод списком… короче они нам не дали такой возможности. Если у стандартных чекеров есть что-то типа BB-кодов [UL][LI][/LI][/UL], то у нас они не работают и даже привычные <b> или <strong> не отрабатывает. Не заслужили, а то еще че-нить поломаем…
Вернемся к описанию возможных параметров объекта уязвимости. Остался только parameter. В нем указывается уязвимый параметр реквеста. Но… возможно, данный параметр как-то связан с другими параметрами уязвимости. По каким-то причинам, мои попытки добавить параметр результатов не дали. Пробовал с добавлением GET-параметров, с реальным job и существующим параметром в запросе - один черт результат никакой. Надеюсь я криворукий и в ближайшее время додумаюсь, в чем ошибка.
Запускаемся
Собирая все в кучу… для запуска создаем файлик в папке C:\ProgramData\Acunetix\shared\custom-scripts\target для Windows или /home/acunetix/.acunetix/data/custom-script/target/ для Linux. Я назвал файлик check_zip.js. В него помещаем код:
JavaScript: Скопировать в буфер обмена
Сохранили. Идем в Acunetix, в Scan Profiles и жмем Add New Profile. Даем любое название и отмечаем галочкой Custom scripts. Жмем Save.
Идем в таргеты, выбираем одну или несколько целей. Создаем новое сканирование и выбираем наш кастомный профиль. Жмем сканировать и ждем, когда увидим подобную картину:
Ура! Чекер готов!
Подобным образом можно творить что угодно. Знаешь какую-то уязвимость в Drupal, которая нет-нет да проскакивает? Супер! Спокойно пишешь чекер и пожинаешь плоды, просто попутно проверяя.
Вместо заключения…
Конечно, это достаточно простой пример и работающий сугубо на собственных запросах. Но, возможно, этот пример поможет глубже разобраться в работе окуня. Даст стимул исследовать этот мощный инструмент.
А развернуться там есть где… можно накидать скриптов другого типа, которые для httpdata, чтобы обрабатывать все респонсы приходящие в окуня. Может есть смысл чекать html-комментарии в исходных кодах страниц и окажется, что админские логины и пароли есть не только в YouTube видео от индусов...
Можно пытаться вытаскивать чувствительные данные, например, из базы данных через SQLi.
Есть темы, в которых и сам еще до конца не разобрался. Например, можно ли и как совместить работу скриптов из target со скриптами из httpdata… Тема практически бесконечная, а главное вы просто подсаживаете простые и легкие чеки к уже проводящимся проверкам.
Источник https://xss.is
Важное замечание: в начальной части статьи дана базовая информация. Если она не интересна, крути вниз до заголовка Custom Scripts, мясо там.
Базовая информация по сканированию в Acunetix
Для начала, давайте разберемся, как вообще работает поиск уязвимостей в Acunetix. Начнем с самого простого - интерфейса пользователя.
Чтобы создать сканирование, у нас есть два пути:
- Выбрать цель или группу интересных целей и жмакнуть “Scan”. В этом режиме у нас доступны всего три параметра: профиль сканирования, и расписание. Все остальное берется из настроек таргета: от частоты запросов, до авторизации, кастомных заголовков и расписания сканирования. Если вы хотите просканировать свой сайт, есть возможность использовать AcuSencor, который значительно повысит эффективность поиска уязвимостей, но вряд ли здесь эта тема будет популярна…
- Отправить POST-запрос к API Axunetix на https://127.0.0.1:3443/api/v1/scans. При этом, мы можем, как минимум, поправить расписание сканирования.
Про работу с API читайте подробнее в моей статье: https://xss.is/threads/114216/ - «Автоматизация Acunetix API + SQLMAP»
Тонкая настройка целей сканирования, при кажущейся простоте, тянет на отдельную статью. Если кому-то будет интересно, обязательно сделаю подробный разбор. Мы же перейдем к профилям сканирования, ведь они определяют какие именно чекеры будут запущены.
В базовой конфигурации, доступны одиннадцать профилей:
К сожалению, информации о том, что за чекеры входят в каждый из профилей, не особо много. Вот, что написано в официальной справке:Full Scan
Critical / High Risk
Critical / High / Medium Risk
Cross-site Scripting
SQL Injection
Weak Passwords
Crawl Only
OWASP Top 10
PCI checks
Sans Top 25
Malware Scan
Нажмите, чтобы раскрыть...
Спойлер: Из справки Acunetix
Full Scan: Использует профиль полного сканирования для запуска сканирования с использованием всех проверок, доступных в Acunetix. Этот профиль обеспечивает наиболее полный охват уязвимостей.
Critical / High Risk: Проверяет только самые опасные веб-уязвимости, такие как межсайтовый скриптинг, SQL-инъекция, включение файлов и т. д. Проверки, включенные в профиль сканирования критических/высоких рисков, динамически генерируются с каждым выпуском для добавления последних критических проверок и проверок высокой степени серьезности.
Critical / High / Medium Risk: Проверяет только веб-уязвимости, которые подвергают сайт критическому и высокому риску взлома, а также неправильные настройки сервера среднего риска и ошибки кодирования сайта. Проверки, включенные в критические, высокие и средние риски, динамически генерируются с каждым выпуском для добавления последних проверок критического, высокого и среднего уровня серьезности.
Cross-site Scripting: Проверяет только уязвимости межсайтового скриптинга. Этот профиль сканирования создается динамически, то есть он обновляется с каждой версией и включает в себя последние проверки.
SQL Injection: Проверяет только уязвимости SQL-инъекций. Этот профиль сканирования создается динамически, то есть он обновляется с каждой версией и включает в себя последние проверки.
Weak Passwords: Этот профиль сканирования определяет формы, которые принимают имя пользователя и пароль. Он будет атаковать эти формы для выявления уязвимостей.
Crawl Only: Только сканирует сайт и строит его структуру без каких-либо проверок на уязвимости.
OWASP Top 10: OWASP Top 10: проверяет только наиболее критические угрозы безопасности веб-приложений, определенные OWASP Top 10.
PCI checks: Проверки PCI: выявляют уязвимости, которые не соответствуют стандартам безопасности данных индустрии платежных карт (PCI).
Sans Top 25: Проверяет только 25 самых опасных ошибок программного обеспечения из списка Common Weakness Enumeration (CWE).
Malware Scan Про это в справке чуть-чуть забыли... но вот что удалось найти на просторах интернет:
Acunetix выполняет сканирование файлов на наличие вредоносных программ, сканируемых Acunetix. Сканирование вредоносных программ Acunetix разработано специально для обнаружения вредоносного ПО JavaScript, которое могло быть внедрено в ваше веб-приложение. Это может произойти при использовании сторонних библиотек, которые могли быть уже заражены, или в результате предыдущего взлома (в ходе которого злоумышленники внедрили вредоносное ПО). Сканирование на наличие вредоносных программ выполняется автоматически при полном сканировании, сканировании уязвимостей с высоким риском или сканировании на наличие вредоносных программ.При установке в Windows Acunetix автоматически использует Защитник Windows. Это программное обеспечение уже установлено вместе с Windows. Никакой дополнительной настройки не требуется.При установке в Linux или MacOS Acunetix использует ClamAV. Вам необходимо установить ClamAV отдельно, и Acunetix автоматически будет использовать его для сканирования на наличие вредоносных программ. В более старых версиях Windows, в которых нет вредоносного компонента Защитника Windows, вы также можете использовать ClamAV.
Черный ящик, к сожалению, пока остается черным ящиком. Меня беспокоит вопрос того, как создаются скрипты, но похоже они загружаются из подкапотной части Acunetix.Беспокоит по причине того, что кастомные скрипты сваливаются в кучу и запускаются тоже кучей. Либо все, либо никто. Если надо какой-то скрипт отключить, придется лезть в его код и «консервировать», а хотелось бы так же выбирать галочкой. Может в будущих версиях поправят, но в 24.2.240226074 все так.
Перейду к созданию своих профилей сканирования. По началу, хватало «Critical/Hight Risk» и «SQL Injection», но разбираясь со специфическими уязвимостями, пришел к выводу, что это не эффективно и нужны более тонко настроенные. Например, если напарсил Wordpress-сайтов, логичным будет создать отдельный профиль сканирования. Для этого иду в Scan Profiles (слева в меню) и жму Add New Profile
Вбиваю название нового профиля «Wordpress Scaner» и в поиске «wordpress», чтобы увидеть все доступные чекеры:
Зачем это нужно? Чтобы сократить время каждого отдельного сканирования и снизить нагрузку на рабочую машину. Если интересен конкретный набор уязвимостей и работаем на количество, зачем затягивать процесс? Проще потом повторно просканить по другим профилям.
Процесс сканирования
Процесс любого сканирования окунем проходит в три этапа:
- Discovery
- Analysis
- Testing
Не важно, какие именно чекеры запускать по профилю скана, окунь проходит все три этапа. Этот момент важен при написании собственных скриптов проверки. Если нужен просто чекер конкретной уязвимости — проще накидать его на том же Python, окунь будет слишком медленным. Он, как минимум, в любом случае будет краулить.
Нажмите, чтобы раскрыть...
Так выглядит результат работы кастомного чекера, который мы скоро напишем. Почти 35 минут и 8000 запросов, при том, что сам чекер делает всего 7 запросов… И чем больше сайт, тем больше времени уйдет на скан.
Для примера, натравил скрипт на пустой index.html, вот какие скрипты он запустил. Синим подчеркнут кастомный скрипт, ради которого и делался запуск.Помимо него, Acunetix выполнил девять чеков. Причем, на время повлияет не только количество страниц и ссылок на сайте, но и используемые технологии.
Поэтому, важно понимать, что чекер может быть лишь добавлением к другим. Но дополнением полезным, так как случайная нелепая уязвимость может стать серьезной точкой опоры.Поэтому, почему бы не повесить тематические чеки на сканы?
Но, вернемся к сканированию. Первым делом, Acunetix имитирует поведение обычного пользователя сайта, чтобы собрать первичную поверхностную информацию: ходит по страницам, прокручивает, тыкает мышкой и т.д.. Для этого используется связка Chromium + Puppeteer. Именно 20 загруженных инстансов Chromium, которые плотно эксплуатируются через Puppeteer, и жрут память и процессорное время.
После этапа сбора информации, окунь анализирует полученные данные. На данном этапе происходят определенные чеки уязвимостей в полученных данных и html-структуре, а так же выстраивается структура предстоящих тестов для третьего этапа. Если говорить о программной части процесса, выполняются скрипты из папки «httpdata», скоро эта информация потребуется.
Этап тестирования. Здесь окунь многократно пытается выполнить вшитые в него чеки, на основе результатов анализа. Если есть куда запихать SQLi и предполагается подобная проверка, будет долбить разными видами инъекций. Нет параметров куда пихать инъекцию или не требуются подобные проверки? Идет на другие чеки. В целом, думаю смысл понятен.
Custom Scripts
В процессе работы с Acunetix, пришло понимание необходимости писать свои скрипты. К счастью, есть абсолютно законный путь. Как пишут в справке окуня, чекеры можно писать на JavaScript, TypeScript или ECMAScript (а есть разница с JS?))) ).
Достаточно написать несложный скрипт, используя предоставляемые Acunetix объекты и положить в одну из папок:
- %PROGRAMDATA%\Acunetix\shared\custom-scripts\ для Windows (обычно %programdata% это C:\ProgramData\)
- /home/acunetix/.acunetix/data/custom-script для Linux
Вернее, это корневые папки для кастомных скриптов, а в этих папках есть подпапки: httpdata и target. Это ключевой момент. В зависимости от того, в какую подпапку положить скрипт, зависит какой тип проверки будет использоваться и на каком этапе, а так же будут доступны разные возможности.
Для примера, в каждой папке лежит по скрипту demo.js, которые неплохо прокомментированы. В них приведены базовые возможности. Для запуска, достаточно высвободить скрипт из блокирующих if (false) и удалив /// disabled: true, создать профиль сканирования включающий Custom scripts и запустить этим профилем скан любого сайта.
Как и писал выше, httpdata относится к анализу информации, полученной в ходе этапа Discoverу. Кастомный скрипт будет применяться ко всем запросам предыдущего этапа. Таким образом, удастся прочекать каждую полученную страницу сайта, json, xml и всему что принимает окунь.
Скрипты из папки target используются для взаимодействия с целью или запросов к другим сервисам. Отсюда будут посылаться запросы, которые в Acunetix реализуются через ax.http.job(). Этот тип скриптов, в отличии от httpdata, запускается только один раз, а не на каждый ответ полученный Acunetix.
Кстати, раз уж затронул, в контексте выполнения скриптов доступны интерфейсы Acunetix: ax, scriptArg и scanState. ax, дает все необходимые методы для взаимодействия с целью. scriptArg позволяет получить аргументы выполнения скрипта и методы связанные с ним. Последний, понятное дело, дает связь с состоянием сканирования. В том числе, добавлением обнаруженных уязвимостей.
Подробнее про ax, scriptArg и scanState
Видимо философия Acunetix предполагает, что пользоваться ПО могут только технически подкованные люди с талантом исследователей. Других причин плохого описания, как API, так и доступных объектов при написании кастомных чекеров, я не вижу.
Часть информации можно почерпнуть из файла native.d.ts, который лежит в папке custom-scripts. Там можно почерпнуть информацию об ax. Файл есть на всех компьютерах, где установлен окунь. Некоторая информация присутствует в справке: https://www.acunetix.com/blog/docs/adding-custom-vulnerabilities/ и все… остальное можно добыть только опытным путем.
Предлагаю посмотреть на интерфейсы, чтобы в целом представлять возможности разработчиков. Для этого, исользую функцию логирования и распечатаю объекты. Вот простой код, который я поместил в папку custom-scripts/target, файл print.js
JavaScript: Скопировать в буфер обмена
Код:
for (key in scriptArg) {
ax.log(ax.LogLevelInfo, `||| ObjectScriptArg ${key} = '${scriptArg[key]}' typeof ${typeof scriptArg[key]}`)
if (typeof scriptArg[key] == 'object') {
obj_key = scriptArg[key]
for (key2 in obj_key) {
ax.log(ax.LogLevelInfo, `||| ObjectScriptArg subobject ${key} ${key2} = '${obj_key[key2]}' typeof ${typeof obj_key[key2]}`)
}
}
}
Вот так выглядит объект scriptArg:
Спойлер: sciptArg
JavaScript: Скопировать в буфер обмена
Код:
location = '[object Object]' typeof object"
location.apiType = 'unknown' typeof string"
location.isAPI = 'false' typeof boolean"
location.parent = 'null' typeof object"
location.fragments = '[object Set]' typeof object"
location.tags = '[object Set]' typeof object"
location.target = '[object Object]' typeof object"
location.sensorDetected = 'false' typeof boolean"
location.schemeCount = '0' typeof number"
location.hasInputs = 'false' typeof boolean"
location.isFolder = 'true' typeof boolean"
location.url = 'http://site.com/' typeof object"
location.name = '' typeof string"
location.parentPath = '/' typeof string"
location.path = '/' typeof string"
location.hasTag = 'function hasTag() { [native code] }' typeof function"
location.inheritsTag = 'function inheritsTag() { [native code] }' typeof function"
location.hasChild = 'function hasChild() { [native code] }' typeof function”
target = '[object Object]' typeof object"
target.sensorDetected = 'false' typeof boolean"
target.root = '[object Object]' typeof object"
target.caseInsensitive = 'true' typeof boolean"
target.server = '[object Object]' typeof object"
target.ip = '211.208.22.25' typeof object"
target.origin = 'http://site.com/' typeof object"
target.url = 'http://site.com/index.html' typeof object"
target.secure = 'false' typeof boolean"
target.port = '' typeof string"
target.host = 'site.com' typeof string"
http = '[object Object]' typeof object"
http.sslInfo = 'null' typeof object"
http.response = 'HTTP/1.1 200 OK"
http.request = 'GET /index.html HTTP/1.1"
http.duration = '131' typeof number"
http.redirCount = '0' typeof number"
http.error = 'null' typeof object"
http.useHTTP2 = 'false' typeof boolean"
http.defaultSession = 'true' typeof boolean"
http.closeOnDone = 'false' typeof boolean"
http.sessionHeaders = 'true' typeof boolean"
http.customHeaders = 'true' typeof boolean"
http.autoRedirect = 'false' typeof boolean"
http.autoHostHeader = 'true' typeof boolean"
http.autoAuthenticate = 'true' typeof boolean"
http.retries = '3' typeof number"
http.timeout = '30000' typeof number"
http.url = 'http://site.com/index.html' typeof object"
http.password = '' typeof string"
http.username = '' typeof string"
http.secure = 'false' typeof boolean"
http.port = '' typeof string"
http.hostname = 'site.com' typeof string"
http.setUrl = 'function setUrl() { [native code] }' typeof function"
http.setSession = 'function setSession() { [native code] }' typeof function"
Большинство свойств понятны без комментариев. По сути, у нас есть location, target и http описывающие контекст взаимодействия с целью. Для примера будут использованы только: scriptArg.location для указания точки уязвимости, а так же два свойства объекта scriptArg.target. Из последнего, мы возьмем хост для настройки запроса к ресурсу и флаг secure, который указывает на необходимость использовать SSL/TLS.
Аналогичным образом получаю scanState и ax:
Спойлер: scanState
check = '[object Object]' typeof object"
check.name = '/custom-scripts/target/check_zips.js' typeof string"
check.firstTime = 'false' typeof boolean"
addVuln = 'function addVuln() { [native code] }' typeof function"
hasVuln = 'function hasVuln() { [native code] }' typeof function"
addLink = 'function addLink() { [native code] }' typeof function"
addExternalLink = 'function addExternalLink() { [native code] }' typeof function"
addHttpRequest = 'function addHttpRequest() { [native code] }' typeof function"
hintLinks = 'function hintLinks() { [native code] }' typeof function"
addHttp = 'function addHttp() { [native code] }' typeof function"
getGlobal = 'function getGlobal() { [native code] }' typeof function"
setGlobal = 'function setGlobal() { [native code] }' typeof function"
findUrl = 'function findUrl() { [native code] }' typeof function"
findPath = 'function findPath() { [native code] }' typeof function"
setTags = 'function setTags() { [native code] }' typeof function"
saveTest = 'function saveTest() { [native code] }' typeof function"
getAtomicCounter = 'function getAtomicCounter() { [native code] }' typeof function"
checkPresence = 'function checkPresence() { [native code] }' typeof function"
addTech = 'function addTech() { [native code] }' typeof function"
Из всех указанных свойств и методов, есть описание исключительно по addVuln (добавление уязвимости и его буду использоватьв в коде), а так же hintLinks, позволяющий передать acunetix массив ссылок о которых иначе он не сможет узнать. Например, скрытые файлы. По всему остальному можно толкьо догадываться. Например, понятное дело, что hasVuln нужен для проверки, может подобная уязвимость уже есть. но что именно принимает - не очень понятно. Или get и setGlobal. Судя по всему, позволяют скриптам взаимодействовать через глобальные параметры... В целом, эта статья и не имеет цели исследовать все возможности каждого интерфейса. В данном случае, это больше справочная информация, чтобы подстегнуть полет мысли.
Самый важный и большой ax:
Спойлер: ax
JavaScript: Скопировать в буфер обмена
Код:
security = '[object Object]' typeof object"
security.listTechnologyIds = 'function listTechnologyIds() { [native code] }' typeof function"
Error httpmutators"
net = '[object Object]' typeof object"
net.tcpSocket = 'function tcpSocket() { [native code] }' typeof function"
net.getHostByName = 'function getHostByName() { [native code] }' typeof function"
util = '[object Object]' typeof object"
util.htmlEncode = 'function htmlEncode() { [native code] }' typeof function"
util.htmlDecode = 'function htmlDecode() { [native code] }' typeof function"
util.base64Decode = 'function base64Decode() { [native code] }' typeof function"
util.base64Encode = 'function base64Encode() { [native code] }' typeof function"
util.md5 = 'function md5() { [native code] }' typeof function"
util.sha1 = 'function sha1() { [native code] }' typeof function"
util.sha256 = 'function sha256() { [native code] }' typeof function"
util.hmac1 = 'function hmac1() { [native code] }' typeof function"
util.hmac224 = 'function hmac224() { [native code] }' typeof function"
util.hmac256 = 'function hmac256() { [native code] }' typeof function"
util.hmac384 = 'function hmac384() { [native code] }' typeof function"
util.hmac512 = 'function hmac512() { [native code] }' typeof function"
util.hmac512 = 'function hmac512() { [native code] }' typeof function"
util.hmacMd5 = 'function hmacMd5() { [native code] }' typeof function"
util.encryptAesCBC = 'function encryptAesCBC() { [native code] }' typeof function"
util.decryptAesCBC = 'function decryptAesCBC() { [native code] }' typeof function"
util.encryptAesECB = 'function encryptAesECB() { [native code] }' typeof function"
util.decryptAesECB = 'function decryptAesECB() { [native code] }' typeof function"
util.hashAll = 'function hashAll() { [native code] }' typeof function"
util.decompressSiteMap = 'function decompressSiteMap() { [native code] }' typeof function"
util.nextSequenceNumber = 'function nextSequenceNumber() { [native code] }' typeof function"
util.pbkdf2_sha1 = 'function pbkdf2_sha1() { [native code] }' typeof function"
util.pbkdf2_sha256 = 'function pbkdf2_sha256() { [native code] }' typeof function"
util.pbkdf2_sha224 = 'function pbkdf2_sha224() { [native code] }' typeof function"
util.pbkdf2_sha512 = 'function pbkdf2_sha512() { [native code] }' typeof function"
util.pbkdf2_sha384 = 'function pbkdf2_sha384() { [native code] }' typeof function"
util.expose_lsr_inputs = 'function expose_lsr_inputs() { [native code] }' typeof function"
util.InjectionType = '[object Object]' typeof object"
util.testForInjection = 'function testForInjection() { [native code] }' typeof function"
util.parseSslCertificate = 'function parseSslCertificate() { [native code] }' typeof function"
util.CompressionAlgorithm = '[object Object]' typeof object"
util.decompress = 'function decompress() { [native code] }' typeof function"
util.compress = 'function compress() { [native code] }' typeof function"
util.compareVersions = 'function compareVersions() { [native code] }' typeof function"
session = '[object Object]' typeof object"
session.acquire = 'function acquire() { [native code] }' typeof function"
session.allCookies = 'function allCookies() { [native code] }' typeof function"
state = '[object Object]' typeof object"
struct = '[object Object]' typeof object"
struct.getHtmlElements = 'function getHtmlElements() { [native code] }' typeof function"
struct.parseXml = 'function parseXml() { [native code] }' typeof function"
struct.parse = 'function parse() { [native code] }' typeof function"
struct.parseResponse = 'function parseResponse() { [native code] }' typeof function"
struct.extractFeatures = 'function extractFeatures() { [native code] }' typeof function"
struct.similarity = 'function similarity() { [native code] }' typeof function"
struct.HtmlElementType = '[object Object]' typeof object"
struct.HtmlFormInputType = '[object Object]' typeof object"
url = '[object Object]' typeof object"
url.encode = 'function encode() { [native code] }' typeof function"
url.decode = 'function decode() { [native code] }' typeof function"
url.absolute = 'function absolute() { [native code] }' typeof function"
url.parse = 'function parse() { [native code] }' typeof function"
url.isURL = 'function isURL() { [native code] }' typeof function"
http = '[object Object]' typeof object"
http.job = 'function job() { [native code] }' typeof function"
http.execute = 'function execute() { [native code] }' typeof function"
http.executeAll = 'function executeAll() { [native code] }' typeof function"
http.executeSeq = 'function executeSeq() { [native code] }' typeof function"
http.parseRequest = 'function parseRequest() { [native code] }' typeof function"
http.is404Response = 'function is404Response() { [native code] }' typeof function"
http.addSensorHeaders = 'function addSensorHeaders() { [native code] }' typeof function"
http.responseCookies = 'function responseCookies() { [native code] }' typeof function"
http.awaitSensorData = 'function awaitSensorData() { [native code] }' typeof function"
log = 'function log() { [native code] }' typeof function"
logd = 'function logd() { [native code] }' typeof function"
logi = 'function logi() { [native code] }' typeof function"
logw = 'function logw() { [native code] }' typeof function"
loge = 'function loge() { [native code] }' typeof function"
exit = 'function exit() { [native code] }' typeof function"
CPPException = 'function CPPException() { [native code] }' typeof function"
AccessViolation = 'function AccessViolation() { [native code] }' typeof function"
loadModule = 'function loadModule() { [native code] }' typeof function"
env = '[object Object]' typeof object"
env.developer = 'false' typeof boolean"
env.AcuMonitor = '[object Object]' typeof object"
env.AcuSensor = '[object Object]' typeof object"
env.licence = '[object Object]' typeof object"
env.http = '[object Object]' typeof object"
env.scanSchemesDetectedDuringLogin = 'true' typeof boolean"
LogLevelDebug = '0' typeof number"
LogLevelInfo = '1' typeof number"
LogLevelWarning = '2' typeof number"
LogLevelError = '3' typeof number"
Собственно, насколько подробная документация у Acunetix, видно невооруженным взглядом. Возможно, когда-нибудь, разберу все доступные методы и свойства. Сейчас же потребуется некий минимум, достаточный для первого полезного чекера.
Логгирование
Как вы поняли, из кода выше, для логирования используется функция ax.log(), которая принимает два аргумента: LogLevel и текст сообщения. Например:
ax.log(ax.LogLevelWarning, 'JOB END')
вернет
2024-05-31T13:21:24.711983 WARN /custom-scripts/target/check_zips.js 18236 JOB END
LogLevel влияет только на значение, которое в логе идет после даты и времени. Нигде в интерфейсе или еще где-то, никаких отличий не увидел. Видимо, это важно при чтении логов через какое-то ПО, которым я не пользуюсь.
Путь к файлу лога можно найти во вкладке Events в карточке сканирования
Внутри архива четыре файла, но принципиально нужный это logfile.csv. Для ускорения поиска своих комментов, открываю файл в Excel и пользую фильтры. Иначе черт ногу сломит, так как запуски сценариев выполняются асинхронно. Даже при том, что свои запросы запускаю синхронно, между ними налетает толпа респонсов от других чеков.
Кстати, по умолчанию, окунь хранит логи за два года... Настройка срока хранения находится в Settings (меню слева)
Чекер забытых админами zip-архивов
Идею подсмотрел здесь же, на форуме. Смысл уязвимости в том, что админы, порой, забывают удалить архивы. В них может быть очень любопытная информация, иногда критическая. Мне попадались полные бэкапы сайтов, включая логины и пароли от баз данных, файлы конфигурации.
Из информации на форуме, а также своего опыта, есть смысл искать следующие файлы: admin.zip, domaincom.zip (с точками без точек и т.д.), dev.zip, site. Понятное дело, что вариаций гораздо больше, но для примера будет достаточно этого, а расширить словарик можно в любой момент.
Сейчас код будет кусками и с отвлечениями на пояснения тех или иных важных моментов, дальше выложу целиком.
Создаю в папке target файл check_zip.js и объявляю нужные константы:
JavaScript: Скопировать в буфер обмена
Код:
const goodContentTypes = ["application/zip", "application/octet-stream"]
const goodStatuses = [200, 206]
const targetHost = scriptArg.target.host
const hostZipNames = new Set([targetHost + '.zip', targetHost.replace('.', '') + '.zip', targetHost.replaceAll('.', '') + '.zip', /[a-zA-Z0-9_-]+\.[a-zA-Z]{2,}$/.exec(targetHost) + '.zip'])
const archNames = ['admin.zip', 'dev.zip', 'config.zip', ...(hostZipNames)]
const result = []
Код берет три жестко заданных названия архивов, а так же от двух до четырех вариаций названий с использованием хоста. Если на вход придет «www.site.com», на выходе будет такой массив:
JavaScript: Скопировать в буфер обмена
Array(7) [ "admin.zip", "dev.zip", "config.zip", "www.site.com.zip", "wwwsite.com.zip", "wwwsitecom.zip", "site.com.zip" ]
Остальные константы, соответственно названиям и значениями. Названия найденных архивов складываю в result, чтобы потом вывести одной уязвимостью все.Конечно, вряд ли бывает, чтобы архивов было много, но решил пиать так.
goodStatuses для чека статуса… насто натыкался на 301-302, которые ведут на какую-то страницу, которую админ сайта решил показывать вместо ошибочных урлов. Бывает еще 403я ошибка, которую выдает WAF, когда видит запрос zip-файла.Это все ненужное и отвлекает.
Но и 200 не гарантирует, что нашелся архив, поэтому что хитросделанные сеошники могут требовать отдавать морду сайта вместо 404. Поэтому проверяю еще и content-type, в обязательном порядке..
Вся последующая работа будет проходить в цикле, который обходит вариации имен файлов:
JavaScript: Скопировать в буфер обмена
Код:
for (archName of archNames) {
…
}
Запросы к цели выполняются через ax.http.job(), который нужно настроить соответствующим образом. Добавить необходимые заголовки, указать используется ли https, указать конкретный путь на сайте и адрес хоста. Да, адрес хоста указывается отдельно, т.к. job не привязывается к таргету и возможны запросы к совершенно другим хостам.
JavaScript: Скопировать в буфер обмена
Код:
let job = ax.http.job();
job.hostname = scriptArg.target.host ;
job.request.uri = '/' + archName;
job.secure = scriptArg.target.secure;
job.retries = 1
job.request.addHeader('Range', 'bytes=0-10240')
В данном случае, адрес хоста и secure берется из scriptArgs. Путь, соответственно, это имя архива в корне сайта, например, ‘/admin.zip’. retries - количество попыток. Можно также добавить заголовок Range, чтобы ограничить количество возвращаемых байт. Архивы бывают достаточно большими, поэтому ограничил 10ю килобайтами. Хотя признаюсь, что добавил заголовок больше для демонстрации.
Запрос готов, остается только запустить запрос в работу методом ax.http.execute в синхронном режиме.
JavaScript: Скопировать в буфер обмена
ax.http.execute(job).sync();
Асинхронно я не пытался запускать, хотя вроде бы ничего не мешает через тот же then прикрутить функцию обрабатывающую результат.
Остается только проверить, успешно ли был выполнен запрос и какой статус ответа. Кроме 200 или 206, ничего не жду, т.к. ищу прямую ссылку на скачивание.
JavaScript: Скопировать в буфер обмена
Код:
if (!job.error && goodStatuses.includes(job.response.status)) {
if (job.response.headers.has('Content-Type') && goodContentTypes.includes(job.response.headers.get('content-type'))) {
result.push(`ZIP Archive found: ${targetHost + '/' + archName}`);
} else {
result.push(`Possibly found zip archive${targetHost + '/' + archName}`)
}
} else {
ax.log(ax.LogLevelError, `[ERROR] ${targetHost + '/' + archName} not found.`);
}
Важный момент, это не путать свойства, которые относятся к самому ax.http.job, какие к ax.http.job.request, а какие к ответу ax.http.job.response. С непривычки, убил час на то, чтобы понять почему скрипт работал неправильно. Как обычно бывает с ошибкой на уровне запятой, уже бился головой об стену и думал, что может проблема в нуленной версии…. оказалось, что неверно проверял status. Пытался получить его из job, хотя надо было проверять job.response.status.
Ниже выдержка из справки по этим объектам. Правда, как показывает практика работы с Acunetix, лучше бы их распечатать и посмотреть, что еще присутствует из недокументированного. Но это не тема нашей сегодняшней телепередачи, иначе просмотр затянется и так вышло не мало.
Спойлер: ax.http.job
hostname: Mandatory. The host or domain name of the server to connect to. Example: www.example.com
secure: Optional. Whether or not the connection should use HTTPS and be secured by TLS/SSL. Can be set to either true or false, with the default being false.
port: Optional. The TCP destination port on which to connect. This setting is optional, and its default value is 80, unless secure is set to true, in which case port will default to 443. The port must be set as a string: job.port = “8080”;
timeout: Optional. The number of milliseconds after which a pending, incomplete connection attempt should be considered as unsuccessful, and cancelled. Must be passed as numeric value. Default: 30000. Example: job.timeout = 10000;
retries: Optional. The number of times a request which has timed out should be retransmitted. Must be passed as numeric value. Default: 3. Example: job.retries = 1;
Спойлер: ax.http.job.request
uri: The URI to request. Default: /. Example: job.request.uri = ‘/path/’;’
method: Usually ‘GET’ or ‘POST’, but arbitrary values are supported. The default value is ‘GET’.
body: The request body to send.
addHeader(name: string, value: string): Allows for the insertion of custom request headers. Example: job.request.addHeader(‘X-Header’, ‘Value’)
Спойлер: ax.http.job.response
version: The HTTP version the server responded with. Example: job.response.version might return the string “HTTP/1.1”
status: The numeric HTTP status code. Common status codes are 200, 301, and 404. An explanation of status codes can be found at https://developer.mozilla.org/en-US/docs/Web/HTTP/Status.
reason: A string representation of the status code. Example: OK
headers: The ax.http.Job.Response.Headers interface gives you convenient access to the received response headers. If a simple text representation of all response headers is preferable, the toString() function can be used: job.response.headers.toString(). To process individual headers, has(name: string): boolean will return information about whether a response header with the given name exists. If so, get(name: string): string may be used to retrieve its value. Example: if(job.response.headers.has(‘Content-Type’)) ax.log(ax.LogLevelInfo, job.response.headers.get(‘content-type’)) – note that header names are not case-sensitive
body: The response body
Что же, запрос скрипт делает, ответ проверяет и складывает в массив. Причем, в двух возможных вариантах. Если контент тайп верный, скрипт уверенно заявляет о правильном ответе. Если нет, скрипт пишет, что возможно есть архив. Хотя это не совсем правильно...
И так, то ради чего все затевалось - добавление вулнерабилити в Acunetix.
JavaScript: Скопировать в буфер обмена
Код:
if (result.length) {
scanState.addVuln({
typeId: 'custom.xml',
location: scriptArg.location,
details: `Archives founded: \n${result.join('\n')}`,
});
}
typeid, по всей видимости, используется для совместимости с движком окуня. По идее, можно подпихнуть здесь другой вулнерабилити, но высока вероятность, что у пациента окончательно поедет крыша и все сломается. Поэтому не мучаем, оставляем custom.xml. Как бы не было смешно, параметр обязательный и всегда один и тот же… Кстати, кастом уязвимости всегда выводится как High Risk с вероятностью 95%.
Хотя, конечно же, верить справке окуня, себя не уважать… но попытки добавить в объект параметр confidence, результато не дали. Пробовал и 100, и 1 и даже строковый 100%. Но, возможно, просто по другому параметр называть надо.
location или path — должен быть указан хотя бы один из этих параметров. Они взаимозаменяемые и взаимодополняющие друг друга. Разница в том, что location должен быть объекмто ax.state.Location. Например, туда втыкают scriptArg.location. path — обычная строка. Например, слэш ('/').
http - очень крутой и очень важный параметр. Благодаря ему в карточках уязвимостей отображаются детальные HTTP Response и HTTP Request. В этом примере параметр опущен, так как является необязательным и абсолютно не имеет смысла для рассматриваемой ситуации. Но если важно видеть запрос и ответ, добавляем в объект уязвимости. Добавление выглядит так:
JavaScript: Скопировать в буфер обмена
Код:
scanState.addVuln({
typeId: 'custom.xml',
location: scriptArg.location,
http: job,
details: `Archives founded: \n${result.join('\n')}`,
});
Т.е. просто передаем отработавший Job и не паримся. Кстати, если надо использовать параметр, то не получится повесить кучу уязвимостей, как в нашем примере. Либо на каждый сработавший запрос создавать по одной Vulnerability. Либо, другой вариант, сохранять один job, создавать для него одну уязвимость и пихать в нее остальные ответы списком. Как, например, это сделано при обнаружении Time-based или Boolean-based SQLi
Синим выделил все запросы, которые сканер выдал просто списком с результатами. Красным пример ответа, составленный из Job.
Кстати, вот это красивое выделение жирным и вывод списком… короче они нам не дали такой возможности. Если у стандартных чекеров есть что-то типа BB-кодов [UL][LI][/LI][/UL], то у нас они не работают и даже привычные <b> или <strong> не отрабатывает. Не заслужили, а то еще че-нить поломаем…
Вернемся к описанию возможных параметров объекта уязвимости. Остался только parameter. В нем указывается уязвимый параметр реквеста. Но… возможно, данный параметр как-то связан с другими параметрами уязвимости. По каким-то причинам, мои попытки добавить параметр результатов не дали. Пробовал с добавлением GET-параметров, с реальным job и существующим параметром в запросе - один черт результат никакой. Надеюсь я криворукий и в ближайшее время додумаюсь, в чем ошибка.
Запускаемся
Собирая все в кучу… для запуска создаем файлик в папке C:\ProgramData\Acunetix\shared\custom-scripts\target для Windows или /home/acunetix/.acunetix/data/custom-script/target/ для Linux. Я назвал файлик check_zip.js. В него помещаем код:
JavaScript: Скопировать в буфер обмена
Код:
const goodContentTypes = ["application/zip", "application/octet-stream"]
const goodStatuses = [200, 206]
const targetHost = scriptArg.target.host
const hostZipNames = new Set([targetHost + '.zip', targetHost.replace('.', '') + '.zip', targetHost.replaceAll('.', '') + '.zip', /[a-zA-Z0-9_-]+\.[a-zA-Z]{2,}$/.exec(targetHost) + '.zip'])
const archNames = ['admin.zip', 'dev.zip', 'config.zip', ...(hostZipNames)]
const result = []
for (archName of archNames) {
ax.log(ax.LogLevelInfo, `[SUCCESS] Prepare JOB to site:${targetHost + '/' + archName} good response.`);
let job = ax.http.job();
job.hostname = scriptArg.target.host;
job.request.uri = '/' + archName + '?el=a';
job.secure = scriptArg.target.secure;
job.request.addHeader('Range', 'bytes=0-10240')
ax.http.execute(job).sync();
ax.log(ax.LogLevelWarning, 'JOB END')
if (!job.error && goodStatuses.includes(job.response.status)) {
if (job.response.headers.has('Content-Type') && goodContentTypes.includes(job.response.headers.get('content-type'))) {
result.push(`ZIP Archive found: <strong>${targetHost + '/' + archName}</strong>`);
} else {
result.push(`Possibly found zip archive${targetHost + '/' + archName}`)
}
} else {
ax.log(ax.LogLevelError, `[ERROR] ${targetHost + '/' + archName} not found.`);
}
lastJob = job
}
if (result.length) {
scanState.addVuln({
typeId: 'custom.xml',
location: scriptArg.location,
details: `Archives founded: \n${result.join('\n')}`,
});
}
Сохранили. Идем в Acunetix, в Scan Profiles и жмем Add New Profile. Даем любое название и отмечаем галочкой Custom scripts. Жмем Save.
Идем в таргеты, выбираем одну или несколько целей. Создаем новое сканирование и выбираем наш кастомный профиль. Жмем сканировать и ждем, когда увидим подобную картину:
Ура! Чекер готов!
Подобным образом можно творить что угодно. Знаешь какую-то уязвимость в Drupal, которая нет-нет да проскакивает? Супер! Спокойно пишешь чекер и пожинаешь плоды, просто попутно проверяя.
Вместо заключения…
Конечно, это достаточно простой пример и работающий сугубо на собственных запросах. Но, возможно, этот пример поможет глубже разобраться в работе окуня. Даст стимул исследовать этот мощный инструмент.
А развернуться там есть где… можно накидать скриптов другого типа, которые для httpdata, чтобы обрабатывать все респонсы приходящие в окуня. Может есть смысл чекать html-комментарии в исходных кодах страниц и окажется, что админские логины и пароли есть не только в YouTube видео от индусов...
Можно пытаться вытаскивать чувствительные данные, например, из базы данных через SQLi.
Есть темы, в которых и сам еще до конца не разобрался. Например, можно ли и как совместить работу скриптов из target со скриптами из httpdata… Тема практически бесконечная, а главное вы просто подсаживаете простые и легкие чеки к уже проводящимся проверкам.