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")
}
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()
}
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()
}
}
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)
}
}
Обфускация и evasion в Go
При всей привлекательности Go‑бинарников для offensive‑задач, их легко анализировать: символы, строки, отладочная информация - всё в открытом виде, что облегчает работу защитникам и реверсерам. Поэтому вокруг языка вырос набор инструментов обфускации и приёмов по уменьшению «шумности» бинарников.Garble - стандарт для обфускации Go‑кода: он оборачивает go build, переименовывает идентификаторы, может шифровать литералы и вычищать лишнюю отладочную информацию. Простейшая «боевитая» команда сборки выглядит так:
Bash:
garble build -ldflags="-s -w" -trimpath -o tool-obf ./cmd/tool
Пример шифрования строки
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))
}
Антидетект и 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 всё ещё кажется, что «писать свой сканер и имплант - перебор», загляни в нашу статью «Нужно ли пентестеру программирование? (Спойлер: да!)» - он хорошо показывает, как даже базовый уровень разработки резко расширяет инструментарий и качество отчётов.
Вложения
Последнее редактирование: