Статья Go для пентестера: быстрые сканеры, утилиты и импланты

1765553089646.webp

Go давно уже перестал быть «языком для бэкенда» и тихо занял своё место в чемодане пентестера рядом с Python‑скриптами и PowerShell‑однострочниками. Кросс‑компиляция, один самодостаточный бинарник, вменяемая производительность и приличная стандартная библиотека - всё это делает его почти идеальным кандидатом для сканеров, sprayer’ов и имплантов. Ниже - глава, которую можно читать как практический гайд: немного теории, короткие шаблоны кода и идеи, куда всё это развивать дальше.

Почему Go стал стандартом​

Go изначально проектировали как язык для сетевых сервисов, и по требованиям он удивительно похож на то, что нужно offensive‑разработчикам: бинарник должен собираться быстро, работать стабильно и не требовать сложного окружения. В отличие от скриптовых языков, Go‑утилиту можно безболезненно таскать по хостам, в том числе без прав администратора и без доступа к интернету.

Кросс‑компиляция в Go встроена в toolchain: достаточно выставить GOOS/GOARCH, и из одного репозитория вы получаете бинарники для Windows, Linux и macOS. В крупных фреймворках вроде Sliver это используется для динамической сборки имплантов под нужную цель, фактически «штампуя» под каждое подключение свой агент.

Go против Python и C​

Python остаётся королём прототипирования, но для доставки на хост начинаются проблемы: зависимостей много, интерпретатор не всегда есть, а packing под EDR - отдельная боль. C, наоборот, даёт прекрасный контроль и маленький footprint, но развитие и сопровождение кросс‑платформенного тулкита на C - это уже работа на полную ставку.

Go оказался посередине: почти как Python по скорости разработки, но с бинарниками уровня C по модели развёртывания. Не случайно тот же Sliver позиционируется как «open‑source Cobalt Strike на Go», опираясь на мTLS, HTTP(S) и DNS‑C2 поверх кросс‑компилируемых агентов.
Посмотреть вложение 81210

Если после этого раздела ты всё ещё сомневаешься, стоит ли вкладываться в Go или вернуться к Python и C, загляни в статью «Языки программирования для кибербезопасности: какие учить в 2025 году [полное руководство с зарплатами]» - там все показано на цифрах и практических кейсах.

Базовые паттерны: сеть, HTTP, concurrency​

Если вы писали на Go только API и веб‑сервисы, offensive‑разработка покажет этот стек под другим углом: внезапно net, net/http и goroutines закрывают 80% задач для пентеста. Сканеры, sprayer’ы и импланты - это во многом переиспользование одних и тех же паттернов.
Самые полезные кирпичики:
  • net.DialTimeout и net.Listen для TCP‑клиентов и простых серверов. ( )
  • http.Client с кастомным Transport: таймауты, прокси, пулы соединений. ( )
  • goroutines + channels как способ построить конвейеры для тысяч запросов без боли с потоками. ( )

Пример TCP‑пробы​

Самый маленький «строительный блок» сканера: проверка одного порта с таймаутом.
Код:
// probe.go
package main

import (
    "fmt"
    "net"
    "os"
    "time"
)

func main() {
    if len(os.Args) != 3 {
        fmt.Printf("Usage: %s host port\n", os.Args[0])
        return
    }
    host := os.Args[1]
    port := os.Args[2]

    addr := net.JoinHostPort(host, port)
    conn, err := net.DialTimeout("tcp", addr, 500*time.Millisecond)
    if err != nil {
        fmt.Println("closed or filtered:", err)
        return
    }
    conn.Close()
    fmt.Println("open")
}
Эта крошка уже полезна, её можно дергать из bash/Python, строя более сложные сценарии, а внутри больших сканеров она становится телом воркера.

Network‑scanner на goroutines​

В Go много маленьких задач, которые отлично масштабируются GOрутинами. Главное - не увлечься и не открыть десятки тысяч соединений одновременно.

Шаблон сканера с ограниченным пулом​

Код:
package main


import (
    "fmt"
    "net"
    "strconv"
    "sync"
    "time"
)


func scan(host string, port int, timeout time.Duration) bool {
    addr := net.JoinHostPort(host, strconv.Itoa(port))
    conn, err := net.DialTimeout("tcp", addr, timeout)
    if err != nil {
        return false
    }
    conn.Close()
    return true
}


func main() {
    host := "192.168.1.10"
    start, end := 1, 1024
    const workers = 100


    ports := make(chan int)
    var wg sync.WaitGroup


    for i := 0; i < workers; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for p := range ports {
                if scan(host, p, 300*time.Millisecond) {
                    fmt.Printf("[+] %d open\n", p)
                }
            }
        }()
    }


    go func() {
        for p := start; p <= end; p++ {
            ports <- p
        }
        close(ports)
    }()


    wg.Wait()
}
Здесь есть всё, что потом перекочует в более серьёзные тулзы: bounded‑concurrency, каналы как очередь задач и таймауты на сетевые операции. Дальше можно добавлять banner grabbing, JSON‑вывод и чтение списка хостов из файла, не меняя архитектуру.
1765553857168.webp

HTTP‑фаззинг и credential spraying​

HTTP‑клиент в Go - почти готовый фреймворк для веб‑фаззинга: стоит прикрутить очереди и немного логики, и у вас уже свой мини‑Burp для конкретной задачи. Credential sprayer по HTTP устроен почти так же, отличие только в том, что варьируются логин/пароль, а не URL или параметры.

Шаблон HTTP‑воркера с rate‑limit​

Код:
package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    client := &http.Client{
        Timeout: 5 * time.Second,
    }

    target := "https://target.local/login"
    ticker := time.NewTicker(time.Second) // 1 запрос в секунду
    defer ticker.Stop()

    creds := [][2]string{
        {"alice", "Winter2025!"},
        {"bob", "Password123!"},
    }

    for _, c := range creds {
        <-ticker.C

        req, err := http.NewRequest("GET", target, nil)
        if err != nil {
            continue
        }
        req.Header.Set("User-Agent", "GoSprayer/0.1")
        // здесь могли бы быть куки/параметры с логином и паролем

        resp, err := client.Do(req)
        if err != nil {
            fmt.Println("error:", err)
            continue
        }
        fmt.Println(c[0], resp.StatusCode)
        resp.Body.Close()
    }
}
Это не полноценный sprayer, но видно два ключевых момента: работа через один http.Client и управление скоростью через Ticker, что важно для обхода lockout‑политик, о которых отдельно пишут в материалах по password spraying.

AD‑spraying: идея и практика​

Для Active Directory Go показывает себя не хуже: тот же gospray использует LDAP‑bind к DC, чтобы тихо проверять пары логин/пароль, не трогая SMB и другие сервисы. Паттерн внутри тот же: очередь задач, goroutines, аккуратные паузы между «горизонтальными» проходами по аккаунтам.

Имплант: C2‑опрос и выполнение команд​

Если смотреть на Sliver и другие C2 через призму «конструктора», имплант - это просто клиент, который периодически стучится на сервер, получает задачи и возвращает результаты. В реальных фреймворках к этому добавляются криптография, профили трафика и сложные транспорты, но базовый скелет вполне читаем.

Шаблон beacоn‑агента​

Код:
package main

import (
    "fmt"
    "os/exec"
    "time"
)

func fetchTask() (string, []string, bool) {
    // В реальной жизни здесь HTTP(S)/DNS‑C2 и парсинг JSON.
    // Для примера просто периодически возвращаем команду.
    return "whoami", nil, true
}

func main() {
    for {
        cmdName, args, ok := fetchTask()
        if ok {
            out, err := exec.Command(cmdName, args...).CombinedOutput()
            if err != nil {
                fmt.Println("err:", err)
            }
            fmt.Println("output:", string(out))
            // здесь обычно отправка результата обратно в C2
        }

        time.Sleep(10 * time.Second)
    }
}
Даже такой игрушечный «beacon» уже иллюстрирует модель, которую Sliver и другие C2 используют в режиме beacon‑коммуникации: периодический check‑in, выполнение команд и возврат результатов. Дальше остаётся заменить заглушку fetchTask на реальный транспорт и добавить шифрование.
1765553883379.webp

Обфускация и evasion в Go​

При всей привлекательности Go‑бинарников для offensive‑задач, их легко анализировать: символы, строки, отладочная информация - всё в открытом виде, что облегчает работу защитникам и реверсерам. Поэтому вокруг языка вырос набор инструментов обфускации и приёмов по уменьшению «шумности» бинарников.

Garble - стандарт для обфускации Go‑кода: он оборачивает go build, переименовывает идентификаторы, может шифровать литералы и вычищать лишнюю отладочную информацию. Простейшая «боевитая» команда сборки выглядит так:
Bash:
garble build -ldflags="-s -w" -trimpath -o tool-obf ./cmd/tool
Флаги -s -w дополнительно выпиливают символы и отладочные секции на уровне линкера, а сам garble минимизирует количество читаемой информации в бинарнике. При этом исследования вроде GoStringUngarbler показывают, что даже такие сборки при желании можно разбирать, так что полагаться только на обфускацию не стоит.

Пример шифрования строки​

API‑ключи, URL C2 и другие чувствительные строки часто шифруют самостоятельно, чтобы не отдавать их в чистом виде ни статическому анализу, ни YARA‑правилам. В Go это выглядит примерно так:
Код:
package main

import (
    "fmt"
)


func xor(data []byte, key byte) []byte {
    out := make([]byte, len(data))
    for i, b := range data {
        out[i] = b ^ key
    }
    return out
}


func main() {
    enc := []byte{0x2a, 0x2d, 0x2c, 0x2c} // сюда помещаем уже зашифрованные байты
    key := byte(0x55)
    dec := xor(enc, key)
    fmt.Println(string(dec))
}
В реальных имплантах всё, конечно, изощрённее: меняются ключи, расщепляются функции, добавляются API‑hashing и прямые syscalls, но даже такой уровень уже убирает строки из «в лоб»‑поиска.
1765553939321.webp

Антидетект и opsec: не только код​

Код - лишь половина истории, вторая половина - операционная дисциплина: инфраструктура, тайминги, уникальность билдов. Sliver, например, динамически компилирует импланты с уникальными сертификатами и ключами на инстанс, чтобы сложнее было делать массовые сигнатуры.
Для своих Go‑утилит имеет смысл:
  • Собирать отдельные бинарники под каждый проект с разными параметрами, а не носить один «вечный» с собой.
  • Вводить jitter и случайные задержки в beacon‑логике, чтобы трафик не выглядел идеально периодическим.
  • Прятать C2‑трафик за легитимными доменами, профилями User‑Agent и путями, а не светить /c2/check с Go‑дефолтами.

Open source‑инструменты на Go​

Если хочется не изобретать велосипед, а посмотреть на «боевой» код, то на Go уже есть достойные открытые проекты. Sliver - мощный C2‑фреймворк, который используют как атакующие, так и защитники для эмуляции угроз и отработки плейбуков. В его репозитории можно найти массу паттернов по организации транспорта, генерации ключей и сборке агентов.

Для более узких задач есть специализированные утилиты: тот же gospray для AD‑spraying показывает, как аккуратно реализовать LDAP‑аутентификацию с concurrency и задержками между раундами. Осмысленное чтение таких репозиториев - отличный способ прокачать и Go, и offensive‑мышление одновременно.

Заключение​

Go в offensive‑разработке - это уже давно как рабочий компромисс между скоростью разработки, удобством доставки и возможностями по маскировке. Простые шаблоны сканера, HTTP‑sprayer’а и beacon‑агента показывают, что входной порог невысокий: достаточно один раз собрать базовую архитектуру и дальше эволюционировать её под свои нужды.

А дальше включается уже та самая экспертиза: понимание AD‑политик, поведения EDR, C2‑паттернов и опsec‑требований, ради которой стоит выходить за пределы готовых фреймворков вроде Sliver и писать свой код. Go здесь не магия, а хороший инструмент и в руках пентестера, и в руках того, кто защищается и хочет понимать, как именно по его сети ходят современные импланты.

Если после гайда по Go всё ещё кажется, что «писать свой сканер и имплант - перебор», загляни в нашу статью «Нужно ли пентестеру программирование? (Спойлер: да!)» - он хорошо показывает, как даже базовый уровень разработки резко расширяет инструментарий и качество отчётов.
 

Вложения

  • 1765553637980.webp
    1765553637980.webp
    24,3 КБ · Просмотры: 46
Последнее редактирование:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab