В этой и последующих статьях мы разберем основные приемы работы с горутинами, иллюстрируя их простыми задачами на перебор значений. Мы не будем сильно погружаться в саму методику перебора — фаззинга, хешкрекинга и так далее. Все‑таки мы учимся кодить, а потому сейчас концентрируемся на инструментах, которые предоставляет язык программирования.
Теории и терминов будет немного больше, чем в предыдущих статьях, потому что тема сама по себе весьма обширная и, скажем прямо, не самая простая. Я постарался изложить материал так, чтобы при первом чтении можно было пропустить совсем скучное, но для лучшего понимания все же советую вникнуть!
Итак, напишем простенькую утилиту для поиска поддоменов (subdomain enumeration) максимально очевидным способом — брутфорсом по списку.
Список несложно найти в интернете, например SecLists или n0kovo_subdomains, но в учебных целях, чтобы не бомбардировать ни в чем не виноватый хост запросами (тем более пока мы не научились ни останавливать горутины, ни управлять их количеством), я советую написать свой буквально из десятка позиций — этого более чем достаточно для начала.
info
Подробнее о поиске поддоменов читай в статье «Веб‑фаззинг с самого начала. Учимся перебирать каталоги и искать скрытые файлы на сайтах».
Создадим новый модуль:
mkdir sudbdomain && cd $_go mod init sudbdomain
touch main.go
В файл main. вставим такой код:
package mainimport ( "bufio" "fmt" "net/http" "os" "strings" "sync" "time")func run() error { const srcFileName = "subdomains.txt" // Целевой хост — аргумент запуска if len(os.Args) <= 1 { fmt.Fprintf(os.Stderr, "Target address not specified\n") os.Exit(1) } host := os.Args[1] // Открываем список поддоменов srcFile, err := os.Open(srcFileName) if err != nil { return fmt.Errorf("opening %s: %w", srcFileName, err) } defer srcFile.Close() // Настраиваем HTTP-клиент client := &http.Client{ Timeout: 1 * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error { // Игнорируем редиректы return http.ErrUseLastResponse }, } // Построчно считываем и проверяем поддомены scanner := bufio.NewScanner(srcFile) for scanner.Scan() { sub := strings.TrimSpace(scanner.Text()) if sub == "" { continue } target := "https://" + sub + "." + host resp, err := client.Get(target) if err != nil { continue } resp.Body.Close() io.ReadAll(resp.Body) if resp.StatusCode == http.StatusNotFound { // 404 нас не интересуют continue } fmt.Printf("%s - %d %s\n", target, resp.StatusCode, http.StatusText(resp.StatusCode)) } if err := scanner.Err(); err != nil { return fmt.Errorf("reading %s: %w", srcFileName, err) } return nil}func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) }}Целевой хост, для которого мы будем искать поддомены, задается аргументом запуска: его значение мы считываем как второй элемент массива os. (первый элемент с индексом 0 содержит имя исполняемого файла). Список поддоменов для опроса считываем из файла. Для упрощения имя файла задано константой srcFileName, но, разумеется, его также можно передавать аргументом, через переменную окружения или задавать в конфиге.
Далее мы используем буферизованное чтение средствами пакета bufio: построчно читаем файл со списком и для каждой строки запускаем проверку доступности хоста. Наконец, мы используем паттерн main-run для удобства обработки ошибок.
При возникновении ошибки выполняется ранний выход из функции run( с возвратом этой ошибки. В функции main( проверяется, возникла ли ошибка, и в этом случае возвращается код 1. При этом корректно обрабатываются отложенные через defer вызовы, чего не произошло бы, если бы os. вызывалась сразу.
info
Удобно работать с конфигами позволяет пакет Viper. Он поддерживает JSON, YAML, INI и множество других вариантов. В качестве альтернативы можно рассмотреть koanf и cleanenv с похожими возможностями. Кроме того, нередко используют GoDotEnv, который позволяет загружать переменные окружения, заданные в файле ..
http.Client
Для сетевого взаимодействия возьмем http. из пакета стандартной библиотеки net/, он позволяет отправлять сетевые запросы и получать ответы. Обрати внимание: мы заранее создаем настроенный экземпляр структуры http. и в дальнейшем работаем с указателем на него — это символ & в определении переменной client. Мы не хотим заново копировать клиент для каждого нового запроса и намеренно используем указатель, чтобы переиспользовать один и тот же экземпляр и тем самым эффективнее расходовать ресурсы. http. потокобезопасен, поэтому мы используем один и тот же экземпляр через этот указатель даже в разных горутинах.
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
