Сегодня я расскажу как автоматизировать атаки на веб‑приложение. Для атак на веб‑приложение можно использовать разные инструменты. Я использую Burp Suite и вот почему.
При автоматизации многошаговых атак прекрасно подходят скриптовые языки, но зачем тратить лишние время на написание и отладку кода, когда есть готовое решение, требующее минимум настроек. Если вы также как и я ленивый юзверь и вам в падлу реализовать такие задачи с помощью языков программирования или тупо жалко время, можете воспользоваться Burp Suite.
Данная утилита предоставляет не один, а несколько способов автоматизации:
- Макросы
- Плагин Stepper
- Плагин Turbo Intruder
Далее, на примере задачи, которую надо решать довольно часто: перебор четырехзначных одноразовых паролей, мы рассмотрим все три варианта. Кстати, за поиск подобных уязвимостей, могут заплатить неплохую денюжку на Bug Bounty
Стендом для выполнения множество повторяющих действий будет одно из заданий ресурса PortSwigger Academy.
Описание задачи PortSwigger Academy
Перевод задачи с сайта PortSwigger Academy:
Двухфакторка в этом задании уязвима для перебора. Вы уже знаете имя пользователя и пароль, но не имеете доступа к верификационному коду пользователя 2FA. Чтобы решить эту задачу, перебором получите код 2FA для доступа к странице аккаунта Карлоса.
Учетные данные жертвы: carlos:montoya.
Сложность этого задании в том, что в данном случае недостаточно просто перебрать код одноразового пароля (OTP) с существующей сессией, ведь это чревато тем, что после 2 неверных попыток сессия будет невалидной. Нам необходимо выполнять предаутентификацию при помощи учетных данных, а после этого попробовать предсказать одноразовый пароль.
Подробнее о задании
Итак, дана страница аутентификации:
При заполнении учетных данных приложение отправляет запрос на сервер:
1 2 3 4 5 6 7 |
POST /login2 HTTP/1.1 Host: ace61ff51f4557d880dbab96004f009d.web-security-academy.net Cookie: session=2gt4P1gFqzyxZJIonAlFv9czYetD5pm0 Content-Type: application/x-www-form-urlencoded Content-Length: 51 csrf=W9Nei8NhTXl5usVKeynuZ3kbjRHaVjW7&mfa-code=1234 |
Если правильно ввести учетные данные, откроется страница ввода одноразового кода.
После ввода случайного OTP-кода приложение отправит следующий запрос:
1 2 3 4 5 6 7 |
POST /login2 HTTP/1.1 Host: ace61ff51f4557d880dbab96004f009d.web-security-academy.net Cookie: session=2gt4P1gFqzyxZJIonAlFv9czYetD5pm0 Content-Type: application/x-www-form-urlencoded Content-Length: 51 csrf=W9Nei8NhTXl5usVKeynuZ3kbjRHaVjW7&mfa-code=1234 |
Если у нас получится угадать одноразовый код, задание будет выполнено. Угадать OTP-код не так уже и трудно, шансов: 1 к 10 000. Если взять в учет то, что мы имеем неограниченной количество попыток, положительный результат нам гарантирован.
На что следует обратить внимание?
- В приложении используется сессионный идентификатор, который предоставляется при входе на сайт. Он изменяется после первого этапа аутентификации при помощи корректных учетных данных.
- После аутентификации у нас есть только две попытки ввода OTP-кода. После двух неудачных попыток наша сессия инвалидируется и приходится начинать весь процесс с начала.
- Приложение использует CSRF-токены, которые меняются при каждом запросе. Их необходимо подхватывать и подменять для каждого нашего POST-запроса.
Осталось автоматизировать процесс получения сессии, ввода первичных учетных данных, подхвата CSRF-токенов и попыток предсказания OTP-кода. Приступим!
Еще по теме: Лучшие расширения для Burp Suite
Способы решения
Автоматизация атаки с помощью макросов
Макросы в Burp Suite — это механизм для автоматизации предопределенных последовательностей действий. Вы можете использовать макросы внутри правил обработки сеансов для решения различных задач. Научиться ими пользоваться несложно, особенно на примере нашей задачи.
- С запущенным Burp Suite входим в систему как Карлос (учетные данные указаны в задаче) и собираем пакеты HTTP-аутентификации вплоть до проверки 2FA. Они пригодятся нам для настройки макроса.
- Поскольку сессия постоянно изменятся и инвалидируется при неуспешных попытках ввода кода, нам понадобится каким‑то образом ее поддерживать. Для этого мы будем использовать возможности обработки сеансов Burp. Перейдем к их настройке.
- В меню Burp перейдите в Project Options → Sessions. На панели Session Handling Rules нажмите кнопку Add. Откроется диалог Session handling rule editor. Здесь мы будем добавлять правила поддержания сессии и ее возобновления.
- В окне диалога во вкладке Scope в разделе URL Scope выберем значение Include All URLs, чтобы не утруждать себя тонкой настройкой. Сессия будет поддерживаться для любых URL.
- Вернемся на вкладку Details и начнем создавать макрос. В разделе Rule Actions нажмем кнопку Add и в открывшемся окошке выберем Run a macro. Здесь и начнется процесс создания макроса, который будет повторяться при отправке каждого нашего запроса.
- В открывшемся окне настройки макроса выберем запросы для автоматизации (это будут запросы первичного входа, получения идентификатора сессии и токена CSRF для отправки первой формы). Для этого в разделе Select macro нажимаем кнопку Add. Здесь мы выбираем пакет входа на страницу /login (GET-запрос), пакет отправки учетных данных с POST-запросом к странице /login и пакет входа на страницу /login2, где Burp подхватит токен CSRF для ввода OTP-кода.
- В этой же вкладке в нижнем правом углу можно протестировать созданный нами макрос, нажав кнопку Test macro. Если мы нажмем эту кнопку, то увидим, что Burp выполнит три запроса подряд, подхватит выданные ему Cookie данные и получит токен CSRF, который надо было использовать для отправки формы. Уже сейчас нам остается только автоматизировать ввод OTP-кода, и дело сделано.
- Нажимаем ОK и закрываем диалоговые окна. Теперь при отправке каждого запроса Burp будет выполнять данный макрос для получения новой сессии, а потом подставлять значение сессии и значение токена CSRF в исходящий запрос для их обновления.
- Теперь возьмем запрос с отправкой OTP-кода (POST-запрос к странице /login2) и отправим его в Intruder для автоматизации.
- Во вкладке Intruder’а оставляем для нагрузки только поле mfa-code (вот так: mfa-code=§1234§) и переходим во вкладку Payloads. Здесь выбираем для Payload Type значение Numbers и указываем цифры, которые мы хотим генерировать: From: 0, To: 9999, Step: 1, Min integer digits: 4.
- Переходим во вкладку Options и настраиваем Number of threads в значение 1 (это нужно потому, что Burp не умеет одновременно поддерживать идентификаторы сессии для двух и более потоков, только для одного).
- После этого запускаем Intruder и включаем режим «ждуна».
На угадывание кода Intruder понадобилось порядка десяти минут (мой код был 0643). Это супердолго! Даже не десятая часть всех попыток. Почему нельзя быстрее? Потому что Session Handling не может поддерживать сессию у двух потоков одновременно.
Подведем итог того, что нам дают макросы и что они умеют.
Возможности:
- удобно поддерживать сеанс сессии, постоянно захватывая идентификатор сессии самостоятельно, даже если он обновляется;
- подхватываются не только значения сессии, но и все значения, которые используются для выполнения нового запроса: переменные, токены CSRF и прочее.
Проблемы:
- работа только в одном потоке;
- если нужны «перекрестные атаки», использующие сессию двух пользователей одновременно, то реализовать это невозможно, так как поддерживается только одна сессия.
- достаточно неочевидная настройка, очень легко запутаться в многочисленных меню.
Автоматизация атаки с помощью плагина Stepper
Плагин Stepper — это бесплатный плагин, доступный в модуле Burp Suite Extender, который помогает автоматизировать последовательности действий. Найти его можно на GitHub.
Разработчики рассказывают о Stepper следующее:
Stepper разработан как естественное развитие инструмента Repeater из Burp Suite, предоставляя возможность создавать последовательности шагов и определять регулярные выражения для извлечения значений из ответов, которые затем могут быть использованы в последующих шагах.
Установим его и воспользуемся им для решения нашей задачи.
Очень важно! Если вы делаете это после предыдущего эксперимента, отключите созданные ранее правила обработки сессий и удалите макросы!
Модуль Stepper позволяет выбрать ряд запросов и объявить в каждом из них переменные, которые запрос получает от предыдущего шага. Затем он подставляет их, а также переменные, полученные из тела ответа при помощи регулярных выражений, и передает следующему запросу. Такая простая и понятная связка.
Шаг 1: Во вкладке Proxy выбираем три запроса, которые нам нужны для получения сессии, первичной аутентификации и извлечения CSRF-токена. Вот они: GET /login, POST /login, GET /login2. Выделив данные запросы, щелкаем на них правой кнопкой мыши и в подразделе Extensions нажимаем кнопку Add 3 items to Stepper > New Sequence. Нам предложат выбрать для этой последовательности название. Я назову ее evil.
Важно: следите, чтобы пакеты были перенесены в правильном порядке! Для этого нужно, чтобы первые пакеты были выше последних при сортировке во вкладке Proxy (это решается сортировкой по номерам пакетов от меньших к большим).
Шаг 2: Переходим в модуль Stepper, который появился во вкладках с остальными модулями.
Шаг 3: Здесь мы увидим нашу последовательность и три пакета, пронумерованные от 1 до 3. Каждый из пакетов мы можем повторно отправить, нажав на кнопку Execute Step, чтобы получить пример тела ответа, и протестировать каждый шаг.
Шаг 4: Выполним первый шаг, нажав кнопку Execute Step. Создадим первую переменную для хранения и передачи идентификатора сессии, нажав на кнопку Add Variable в нижнем правом углу модуля. Назовем переменную session и добавим ей условие поиска в поле Condition: session=([\<wbr />d\<wbr />w]<wbr />+). Таким образом у нас появится первая переменная сессии, которую мы будем пробрасывать по остальным запросам и использовать повторно.
Шаг 5: Также добавим вторую переменную токена CSRF, которую пробросим на следующий запрос отправки учетных данных. Нажмем кнопку Add Variable, назовем переменную csrf и добавим условие нахождения ее в теле ответа в поле Condition со следующим значением: name="csrf" <wbr />value="([\<wbr />w\<wbr />d]<wbr />+)<wbr />"».
Вот что получилось у меня после выполнения этих шагов.
Шаг 6: Теперь можно перейти к следующему запросу отправки учетных данных и использовать в нем переменные session и csrf. Для этого переходим к следующему шагу (Step 2) и вместо существующих там значений сессии и CSRF-токена подставим обращение к переменным в следующем виде: $VAR:<wbr />session$ и $VAR:<wbr />csrf$. Получится что‑то вроде этого:
1 2 3 4 5 6 7 |
POST /login HTTP/1.1 Host: ac3f1f861fe209fb80374867009900fe.web-security-academy.net Cookie: session=$VAR:session$ Content-Type: application/x-www-form-urlencoded Content-Length: 70 csrf=$VAR:csrf$&username=carlos&password=montoya |
Шаг 7: Выполним этот второй шаг нажатием на кнопку Execute Step и получим ответ, где нас попытаются перенаправить на страницу /login2 и выдадут нам новый идентификатор сессии, который нам снова нужно захватить при помощи регулярных выражений и передать на следующий шаг № 3. Создаем такую же переменную session как и в пункте 4, и переходим к шагу № 3.
Шаг 8: На шаге № 3 не забываем снова изменить значение сессии на переменную $VAR:session$ и выполняем запрос, так как нам просто нужно получить токен CSRF для последнего шага. После выполнения запроса снова добавляем парсинг токена CSRF в виде переменной csrf, как мы это делали в пункте 5 ранее.
Шаг 9: Теперь мы можем попробовать выполнить всю последовательность и проверить ее работоспособность. Нажмем на кнопку Execute Sequence в самом низу окна модуля. Видим, что последовательность выполнилась корректно и на последнем шаге мы получаем ответ с предложением ввести OTP-код.
Теперь наша задача запустить данную последовательность 10 тысяч раз. Для этого переносим POST-запрос /login2 из вкладки Proxy в Intruder.
Шаг 10: В панели Intruder нам нужно убрать символы подстановок § в полях сессии и CSRF-токена и оставить подстановку только в поле mfa-code вот так: mfa-code=§1337§.
Шаг 11: Для того чтобы шаги нашей последовательности из модуля Stepper выполнялись для каждого запроса из Intruder, добавим в заголовки запроса следующее: X-Stepper-Execute-Before: [Название вашей последовательности]
Шаг 12: Также подставим имена наших переменных $VAR:session$ и $VAR:csrf$ в пакет Intruder, только исправим их на $VAR:[Название вашей последовательности]:session$ и $VAR:[Здесь тоже]:csrf$. В Intruder у меня получился следующий пакет запроса:
1 2 3 4 5 6 7 8 |
POST /login2 HTTP/1.1 Host: ac311f2c1f2abcbd807689da0068009a.web-security-academy.net Cookie: session=$VAR:evil:session$ Content-Type: application/x-www-form-urlencoded X-Stepper-Execute-Before: evil Content-Length: 51 csrf=$VAR:evil:csrf$&mfa-code=§1337§ |
В данном примере имя моей последовательности — evil.
Теперь перед каждым запросом из Intruder будет выполняться последовательность ранее подготовленных запросов, которые передадут в пакет полученные значения сессии и CSRF-токена.
Шаг 13: Последний шаг, настраиваем нагрузку во вкладке Payloads, точно так же, как мы это делали в предыдущем разделе. Выбираем в Payload Type значение Numbers и указываем цифры, которые мы хотим генерировать: From: 0, To: 9999, Step: 1, Min integer digits: 4.
Шаг 14: Запускаем нашу атаку! Отслеживать отправленные пакеты можно в появившейся вкладке Logger или при помощи модуля Logger++.
В этот раз мне повезло больше, мой код был 0261. Что важного можно заметить? В отличие от предыдущего варианта мы не ограничены одним потоком и создали пять потоков, а самые умные могли отключить галочку Set Connection: close в опциях нагрузки и удалить этот заголовок из пакетов в Stepper и Intruder, чтобы увеличить скорость работы.
Сделаем выводы.
Возможности:
- из‑за того что модуль Stepper поддерживает сессии, передавая значение сессии и токена от запроса к запросу, мы можем использовать многопоточное исполнение запросов и наши переменные не будут конфликтовать в потоках;
- нам становятся доступны перекрестные атаки, когда мы можем запускать работу нескольких последовательностей параллельно;
- нативно понятная настройка переноса состояний от запроса к запросу и легко добавляемый заголовок X-Stepper-Execute-Before:, который запускает Stepper для любого модуля.
Проблемы:
- на самом деле Stepper не позволяет использовать так много потоков, как хотелось бы. Около трех потоков действительно успевают работать вместе, но из‑за особенностей кода модуля большее их количество только тормозит выполнение;
- приходится вручную настраивать переменные для каждого запроса, что может выглядеть изнуряюще скучно.
Данный плагин подходит скорее для использования его с модулем Repeater, о чем разработчики предупреждали нас в приветственном сообщении.
Автоматизация атаки с помощью Turbo Intruder
Turbo Intruder — один из самых мощных инструментов Burp Suite, и его должен освоить каждый уважающий себя пользователь Burp. Его также можно скачать с GitHub.
Turbo Intruder представляет собой расширение Burp Suite для отправки большого количества запросов HTTP и анализа результатов. Оно призвано дополнить Burp Intruder, обрабатывая атаки, требующие исключительной скорости, продолжительности или сложности. Этот модуль отличают следующие особенности.
- Быстрота: Turbo Intruder использует стек HTTP, созданный вручную с нуля с учетом скорости. В результате на многих целях он может серьезно обогнать даже модные асинхронные скрипты на Go (на самом деле стек можно выбрать, и большинство из них будут вам знакомы).
- Масштабируемость: Turbo Intruder может достичь плоского использования памяти, что позволяет проводить надежные многодневные атаки. Его также можно запускать в headless-окружении через командную строку.
- Гибкость: Атаки конфигурируются с помощью Python. Это позволяет выполнять сложные требования, например подписанные запросы и многоступенчатые последовательности атак. Кроме того, пользовательский стек HTTP позволяет обрабатывать неправильно сформированные запросы, которые ломают другие библиотеки.
- Удобство: скучные результаты могут быть автоматически отфильтрованы с помощью усовершенствованного алгоритма дифов, адаптированного из Backslash Powered Scanner. Это означает, что вы можете запустить атаку и получить полезные результаты в два клика.
Для использования Turbo Intruder необходимо знание основ Python. Тем не менее все, что нам нужно для начала, — это установить Turbo Intruder из модуля Extender.
После установки сразу же перейдем к решению задачи.
- Выберем самый первый пакет в последовательности во вкладке Proxy (пакет GET /login) и нажмем на него правой кнопкой мыши. А дальше выберем пункт Extensions → Send to turbo intruder.
- В открывшейся панели Turbo Intruder появится запрос и примеры скриптов, которые можно выбрать для использования и модификации. В данном случае все, что нам нужно для победы, — это написать скрипт, который позволит решить задание. Ниже я приведу свой пример кода и объясню логику скрипта (сделайте мне скидку на качество, вспомнив о том, что пентестеры не умеют кодить):
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 |
import re import time # Регулярки для вытаскивания идентификаторов сессии и CSRF-токенов re_csrf = 'name="csrf" value="([\w\d]+)"' re_session = 'session=([\d\w]+)' iterable = 0 def queueRequests(target, wordlists): global engine # Включаем один запрос на один коннект, чтобы не нарушать логику выполнения, коннекты в соответствии с тем, что выдержит приложение. # Все эти значения придется калибровать от сервера к серверу. Сервер задачи не очень хорошо держит высокую нагрузку, поэтому ограничимся пятью параллельными соединениями engine = RequestEngine(endpoint='https://ac051f441e762a3780359cb6002300a2.web-security-academy.net:443',concurrentConnections=5,requestsPerConnection=1) # Запускаем первые запросы, которые спровоцируют запуск последующих запросов. # Делаем задержку в одну секунду, чтобы потоки не исполнялись синхронно, а чередовались. for x in xrange(1,6): print '1. GET /login Request' engine.queue(target.req,'') time.sleep(1) def handleResponse(req, interesting): global engine global iterable if 'Location: /my-account' in req.response: # Если мы в ответе получили данный заголовок, значит, мы победили table.add(req) print 'You Win!' return None if 'Incorrect security code' in req.response: # Если мы получили в ответе сообщение о неправильно введенном коде, значит, мы использовали одну попытку, и тогда мы запускаем новую итерацию запросов table.add(req) print '1. GET /login Request' engine.queue(target.req,'') return None if 'Please enter your 4-digit security code' in req.response: # Если в ответе мы получаем предложение ввести ОТР, то отправляем запрос с попыткой ввода ОТР match_csrf = re.search(re_csrf, req.response) match_session = re.search(re_session, req.getRequest()) req = '''POST /login2 HTTP/1.1\r\nHost: ac051f441e762a3780359cb6002300a2.web-security-academy.net\r\nCookie: session=%s\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 51\r\n\r\ncsrf=%s&mfa-code=%s''' print '4. POST /login2 Request' engine.queue(req, [match_session.group(1),match_csrf.group(1),str(iterable).zfill(4)]) iterable += 1 print 'Iterable: ' + str(iterable) return None if 'Location: /login2' in req.response: # Если в ответе мы получаем сообщение о переходе на страницу /login2, значит, мы ранее ввели корректные креды и теперь получаем новый идентификатор сессии и переходим на страницу для взятия CSRF для запроса с OTP match_session = re.search(re_session, req.response) req = '''GET /login2 HTTP/1.1\r\nHost: ac051f441e762a3780359cb6002300a2.web-security-academy.net\r\nCookie: session=%s\r\n\r\n''' print '3. GET /login2 Request' engine.queue(req, match_session.group(1)) return None if ' |
Так как в Turbo Intruder нет возможности удобно поддерживать сессию между запросами, приходится делать это руками, иногда просто создавая новые запросы на основе полученных идентификаторов сессии из последнего ответа, иногда на основе данных сессии, которые в запросе ранее.
В первом приближении логика работы скрипта такова. Я запускаю пять первичных запросов, которые выполняются в пяти параллельных соединениях. Далее ответ на каждый запрос обрабатывается. Обработчик ответов ставит условие на то, что получил ожидаемый ответ, и дальше выполняет следующий по логике запрос. Например, после получения ответа с приглашением ввода пароля выполняется запрос ввода логина и пароля и так далее.
При помощи данного скрипта мне удалось выполнить 400 попыток (~1500 запросов) за 30 секунд и решить задание примерно в 20 раз быстрее, чем в прошлых примерах. Если быть честным, то можно было потратить чуть больше времени на калибровку параметров concurrentConnections, requestsPerConnection и pipeline и решить задачу еще быстрее, но мне хватило и этого.
Подведем итоги для этого примера.
Возможности:
- Turbo Intruder может выжать из приложения максимальную скорость;
- поскольку мы пишем код на Python, можно заложить в инструмент логику почти любой сложности;
- крайне удобно фильтровать выполненные запросы в таблице результатов, а также можно задавать свои поля к запросам для сортировки и фильтрации.
Проблемы:
- нужно писать код, и это мало отличается от написания скриптов с нуля, хоть и дает большое преимущество в использовании подготовленных абстракций для многопоточного и параллельного выполнения запросов;
- нет документации к инструменту, кроме ряда примеров, которых вам должно хватить;
- в наиболее быстрых движках запросов для Intruder запросы и ответы не логируются в модули Logger или Logger++, что не позволяет удобно просматривать происходящее в сети. Приходится использовать способы отладки, встроенные в сам Turbo Intruder и его абстракции.
Заключение
Лично мне импонирует инструмент Turbo Intruder, но для новичков модуль Stepper или встроенные макросы могут оказаться проще в использовании. Тем не менее для реальных задач макросы и Stepper могут не подойти из‑за своей медлительности.
Также стоит сказать, что в каждом примере я оставил несколько путей улучшения скорости работы или увеличения количества попыток примерно в два раза при незначительном увеличении количества запросов. Если вы придумаете улучшения, поделитесь ими в комментариях. Кроме того, будет здорово, если вы расскажете нам о других вариантах решения этой задачи.
Еще по теме: Как искать уязвимости на сайтах
Вопрос .Как сделать так , чтобы в tor браузер был постоянный ip адрес ..У меня проблема в том , что так как на один сайт я постоянно захожу с разных ip , то всё время приходится подтверждать личность (приходит код на телефон ) как сделать так , чтобы всё время был 1 ip на сайте?
Спасибо автору статьи