СКАНЕР RDP ДОСТУПА ТЕХНИЧЕСКОЕ ЗАДАНИЕ ЦЕЛЬ Необходимо написать модуль сканера и брут-форма доступа по протоколу RDP. Модуль должен быть оформлен в соответствии с правилами, приведенными в документе modules_HOWTO. РЕАЛИЗАЦИЯ Модуль состоит из сканера, детектора имени, и подмодуля брут-форса. У сканера есть два режима: 1) режим брут-форса 2) режим проверки В режиме брут-форса сканер самостоятельно ищет сервера с открытым портом RDP, определяет имена пользователей и подбирает к ним пароли. Результат работы - список адресов, имен и паролей. В режиме проверки сканер работает по уже известному списку адресов серверов, имен и паролей. В этом режиме проверяется актуальность списка, а также окружение и возможности сервера. В режиме брут-форса: Сканер делает перебор доменов и портов, используя получаемый извне список доменов для работы. Результат работы сканера - это список адресов и портов, которые точно определены как сервис RDP. Детектор имени получает на вход адрес:порт сервиса RDP. Резульат работы детектора - список имен пользователей данного RDP в текстовом виде. Модуль брута получает на вход адрес:порт, список имен пользователей для данного адреса, и словари. Результат работы модуля брута - список подобранных паролей. В режиме try-brute: То же, что brute, но используется словарь имен пользователей. В режиме NOP (холостой ход) сканер ничего не делает, только ожидает новых настроек. Назначение режима холостого хода: - аварийный останов сети, например если обнаружились неверные настройки или неверное поведение сканера - пауза для того, чтобы можно было выставить настройки, без немедленного их запуска на выполнение. Сканер взаимодействует с управляющим сервером для получения от него настроек, словарей, доменов для проверки итд. Получить режим работы можно HTTP-запросом на сервер GET ///rdp/mode HTTP/1.1 В теле HTTP-ответа модуль ожидает строку brute, check, trybrute или nop. Любое другое значение некорректно - в таком случае модуль делает повторные запросы каждые 5 минут; до получения корректного ответа работа модуля не начинается. Изменение режима возможно только с NOP и на NOP. Переход, к примеру, brute -> check невозможен. В этом случае сканер переходит в режим NOP и отправляет на сервер сообщение об ошибке. В случае работы в режиме NOP, сканер каждые 10 минут делает запрос режима и настроек. Админка сканеров автоматически переводит режим в NOP при отработке 100% чанков всеми ботами (и только в этом случае - когда нету сомнений в завершении работы). СКАНЕР Сканер получает список доменов для проверки HTTP-запросом на сервер GET ///rdp/domains HTTP/1.1 Значения group и clientid - это поля struct ParentInfo CHAR ParentID[256]; CHAR ParentGroup[64]; (см. module_HOWTO) В режиме брута формат ответа: адрес1:порт[\r]\n адрес2:порт[\r]\n ... (одна или множество записей) В режиме проверки формат ответа ip:port@username:password[\r]\n ... (одна или множество записей) Полное сканирование портов делать не следует, берем только стандартные порты RDP +-10 портов вверх-вниз. Адрес:порт выдается на выход сканера только в том случае, если удается соединиться с данным портом и есть признак того, что это RDP-соединение. При завершении перебора по выданному списку мы даем знать об этом серверу: GET ///rdp/over HTTP/1.1 Ответ сервера - такой же, как на запрос /domains - новый список доменов для работы. При неожиданном ответе (пустой список, код ошибки итд) модуль переходит на холостой ход (сканирование остановлено) и делает тот же самый запрос раз в 10 минут (время - в константу). Старые версии сканера не поддерживали правила словарей. Чтобы отличить старую версию сканера от новой, новая версия сканера во все GET-запросы добавляет HTTP-заголовок fmode: 1 ОПРЕДЕЛЕНИЕ ИМЕНИ ПОЛЬЗОВАТЕЛЯ RDP При успешном соединении, модуль делает скриншот списка пользователей RDP. Далее софт распознает скрин и форматирует в текст. Сформатированный текст редактирует под формат ip:port@username. Если несколько пользователей на одном ip, то выводит в список наподобие: 147.126.54.43:3900@username1 147.126.54.43:3900@username2 147.126.54.43:3900@username3 147.126.54.43:3900@username4 и передает на вход брут-форсера. БРУТ RDP Словарь для перебора получаем HTTP-запросом к управляющему серверу: GET ///rdp/dict HTTP/1.1 В ответ нам приходит словарь либо как text/plain, либо application/gzip (смотрим на заголовок ответа Content-Type) Если упаковка в gzip, то после распаковки мы ожидаем такой же формат словаря, как для простого текста: - одно слово на строку, разделитель строк может быть \n или \r\n. Словарь паролей поддерживает шаблоны паролей (т.е. возможность подставить макрос из текущего контекста работы). Примеры макросов и правил: %EmptyPass% // empty password. %GetHost% // get host name from dns server. Slow speed! %IP% // get ip (example: 192.168.0.1 = 192.168.0.1) %Port% // get port (example: 192.168.0.1:3389 = 3389) и так далее - всего имеется несколько десятков правил. Подробное описание правил шаблонов приведено в документации к модулю. При разработке брута нужно соблюсти следующий компромисс: - число потоков подбора должно быть максимально для данного компьютера (см. thread_concurrency в STL) - потоки не должны мешать интерактивным задачам, т.е. должны как минимум иметь пониженный приоритет (см. SetThreadPriority()) - поток не должен долбить в один и тот же адрес постоянно. Вместо того, чтобы перебирать словарь по одной комбинации адрес:порт:юзер, лучше взять список из 100 адресов и перебирать их по очереди, чтобы был баланс между интервалом между запросами на один и тот же хост, и КПД софта - в то же время, размер списка хостов для перебора для каждого потока должен быть не слишком большим, дабы не был заметен сетевой трафик на не связанные между собою узлы - в то же время, чем выше случайность, тем лучше - никогда не повторять один и тот же хост может быть выгодней, чем работать по одному и тому же хосту. Все размерности интервалов, таймаутов, размеров пачки адресов для перебора итд должны быть вынесены в глобальный файл config.h в виде констант. Этот баланс нужно выяснить экспериментально; исходим от самой наивной реализации и далее усложняем. В режиме сканирования на сервер в качестве результата отправляются только адреса, к которым удалось подобрать пароль. В режиме проверки отправляются все адреса из входного списка, которым присваивается тег и проставляются доп.поля. Отправка делается по протоколу DPOST (см. "ТЗ граб паролей DPOST" для описания протокола) запросом POST ///rdp/81 HTTP/1.1 Данные отправляются в контейнере multipart/form-data с полями source и data. Значение поля source - "RDP Passwords" Значение поля data: простой текст, разделитель строк \r\n Формат записи: rdp|
:||||||...\r\n ... (одна или множество записей) Здесь поля address:port, username, password определяются в результате сканирования, а поля tag и field1... - в результате пост-проверки (см.ниже). Поля field1=value записываются в виде ключ=значение, например ...|subnet=192.168.1.255|netmask=255.255.255.0|итд... Такой формат позволяет передавать произвольные поля, не слишком меняя парсер и обратную совместимость. Частоту отправки намайненных данных можно получить с управляющего сервера HTTP-запросом GET ///rdp/freq HTTP/1.1 В теле ответа мы ожидаем число - это число секунд, не чаще которого следует отправлять данные. Если это 0 - отправка сразу по готовности (для режима скана - как только найден новый пароль; для режима проверки - как только обработан следующий адрес из списка) Если это положительное число - мы накапливаем записи в буфере и отправляем раз в X секунд, очищая буфер при успешной отправке. СОБЫТИЯ Модуль должен отправлять следующие события через callback (см. "module_HOWTO"): - RDP scanner build %date% %time% started - %d addresses tried, %d RDP hosts detected, %d passwords found - периодически раз в полчаса (таймаут задается константой в config.h) - RDP password found: %addr%:%port%:%username%:%password% - при нахождении пароля КОНФИГИ Конфиги передаются в модуль через вызов Control(). Название конфига - это строка в аргументе Ctl, тело конфига - аргумент CtlArg, длина конфига - CtlArgLen (см. "module_HOWTO") Модуль получает единственный конфиг с именем srv, содержащий список адресов управляющего сервера, разделенных \r\n или \n, в формате адрес:порт. Если порт четный, работа идет по HTTP, если нечетный - HTTPS. Если указан префикс протокола (http/https), префикс имеет приоритет над указанным портом. Модуль работает с тем управляющим сервером, до которого удалось достучаться первым, по каждому запросу. ПОЛУЧЕНИЕ ИНФОРМАЦИИ О ХОСТЕ Нужно предусмотреть автоматическое получение информации о хосте, как в режиме брута после подбора пароля, так и в режиме проверки. На каждый подобранный пароль, софт устанавливает соединение, определяет локацию, права пользования, операционную систему. <УСТАРЕЛО> //Открывает CMD, вводит команду: 1 - net view, если ответ //о системной ошибке, данный хост отмечается как no network, если ответ с списком //компьютеров, то вводится команда 2 - net group "Domain Computers" /DOMAIN, //если ответ о системной ошибке, данный хост отмечается как not in domain, //если положительный ответ, то вводится команда 3 - nltest /domain_trusts /all_trusts, //если есть инфа с доменами, то скачивается https://www.sendspace.com/file/172iky, //распаковывается, запускается файл с форматом .bat, bp появившегося результат //в той же папке находит файл subnet, копирует с него информацию, и вставляет //в комментарий этого хоста, при этом этот хост помечается как in domain. //Все эти пометки и дополнительную информацию следует передавать в расширенных полях при передаче данных по DPOST. Далее идет цитата требований заказчика, без изменений - ниже мои комментарии и пояснения: 1) Загружается список ip:port@username:password автоматом с результата брута 2) Софт устанавливает соединение. Те, с которыми не смог установить соединение определяет причину (закрыт доступ или просто сервер в off). Если сервер закрыт для доступа, не подошел пароль или имя пользователя, то убирает в раздел BAD RDP. Если сервер просто находится в off, то убирает в раздел OFF RDP. Если коннект установлен, определяет локацию, права пользования, операционную систему, имя компьютера, вносит в раздел ONLINE RDP. раздел ONLINE RDP делится на две вкладки IN DOMAIN и NOT DOMAIN в разделе IN DOMAIN есть такие ячейки: subnets, ad_users, ad_computers, ad_ous, ad_group, trustdmp, domainlist 3) в cmd вводит команду whoami/upn. если ответ "имя пользователя"/"домен" - то рдп попадает в вкладку ON DOMAIN. "домен" записывает в ячейку domainlist если ответ error, то рдп размещыется в вкладку NOT DOMAIN 4) берет рдп с раздела ON DOMAIN и туда скачивает файлы adf.bat, adfind.exe и XXX.exe в архиве, далее распаковываем 5) запускает файл adf.bat. создаются текстовые файлы с именами: subnets, ad_users, ad_computers, ad_ous, ad_group, trustdmp, domainlist из текстовых файлов subnets, ad_users, ad_computers, ad_ous, ad_group, trustdmp берем последнюю строку с количеством объектов (Objects returned) и количество их записывается по соответствию "имя txt файла=имя ячейки" из txt файла domainlist все домены записываем в ячейку domainlist 6) запускаем XXX.exe от имени администратора. если не отрабатывает файл от имени администратора, то запускаем простым методом. в админке отмечается от каких прав запущен XXX.exe если файл не возможно запустить по причине блокирования или АВ режет или вообще при распаковке АВ удалил XXX.exe, то все это отражается комментарием в админке 7) Далее берутся рдп с раздела NOT DOMAIN скачивается архив с файлом XXX.exe, распаковывается запускаем XXX.exe от имени администратора если не отрабатывает файл от имени администратора, то запускаем простым методом. в админке отмечается от каких прав запущен XXX.exe если файл не возможно запустить по причине блокирования или АВ режет или вообще при распаковке АВ удалил XXX.exe, то все это отражается комментарием в админке То есть, мы запускаем команды (часть встроено в ОС, часть нужно скачать из сети - команду adfind), анализируем ответы, и проставляем поля tag (их на данный момент 4: bad rdp, off rdp, online rdp in domain, online rdp not in domain) и доп.поля, полученные из запускаемых команд. В режиме брута - для каждого подобранного пароля. В режиме проверки - для каждого хоста из списка сканирования. ОФОРМЛЕНИЕ Следует внимательно читать "module_HOWTO" и внимательно относится к указанным там требованиям по использованию библиотек, запретам, мерам по обфускации строк, системных вызовов, логированию, формату сборок итд. ИНТЕРФЕЙС ПОЛЬЗОВАТЕЛЯ Один и тот же модуль будет использоваться и как .dll, и как интерактивный софт с GUI. При этом вся логика должна быть спрятана внутри rdp.dll, а интерфейс должен быть внешней (отдельной) программой, использующей функции rdp.dll. Напоминаю, что rpp.dll - это модуль, экспортирующий 4 функции Start, Control, FreeBuffer, Release, и все взаимодействие с внешним миром происходит только через них. Соответственно, конфиги модуль получает через вызовы Control, настройки, словари, список для сканирования модуль получает с HTTP-сервера (который нужно организовать в GUI); GUI получает обратную связь от модуля (сообщения о событиях) - через вызовы callback (см.описание функции Start). В GUI должно быть предусмотрено: - задание каждого конфига - переключение режима (check/brute) - старт и стоп работы в текущем режиме. Допустим как неинтерактивный режим (консольная программа с управлением через командную строку - но тогда управление должно быть исчерпывающим), так и оконная - тут допустим как WinAPI, так и QT. Программу можно запилить на C# для простоты разработки. Организовать взаимодействие GUI с модулем можно так: - задаем адрес управляющего сервера - 127.0.0.1:порт GUI - запускаем управляющий сервер в GUI, который умеет отвечать на запросы конфигов и принимать найденные пароли - выдаем в модуль адрес своего callback, для получения событий из модуля - далее весь ввод пользователя и его действия мы преобразуем в конфиги, которые будем отдавать модулю - конфиги должны формироваться перед выдачей Start(); во время работы должны блокироваться все настройки GUI, кроме кнопки СТОП - раскладывать проверенные записи по спискам (bad rdp, off rdp итд) нужно на основании поля tag.