GO-SECURITY
Authorized use only. Offensive reference for systems you own or are explicitly permitted to test. You are responsible for staying within the law.
Go basics for building security tools. Static binaries, built-in concurrency, and fast compilation — the language behind many modern offensive and defensive security tools.
WHY GO FOR SECURITY#
- Compiles to single static binary (no dependencies) - Cross-compilation built-in (GOOS/GOARCH) - Goroutines for massive concurrency (scanning, fuzzing) - Strong standard library (net, crypto, os) - Most modern security tools written in Go - Tools: Nuclei, Subfinder, Gobuster, Sliver, Chisel, Ligolo
PROJECT SETUP#
# Install Go # https://go.dev/dl/ # New project mkdir mytool && cd mytool go mod init mytool # Build go build -o mytool . # Build go build -ldflags="-s -w" -o mytool . # Stripped binary go run main.go # Build and run # Cross-compile GOOS=windows GOARCH=amd64 go build -o mytool.exe . GOOS=linux GOARCH=amd64 go build -o mytool . GOOS=darwin GOARCH=arm64 go build -o mytool . # Static binary (no CGO) CGO_ENABLED=0 go build -ldflags="-s -w" -o mytool .
NETWORKING#
package main
import (
"fmt"
"net"
"time"
)
// TCP client
func tcpClient() {
conn, err := net.Dial("tcp", "10.10.10.5:80")
if err != nil { panic(err) }
defer conn.Close()
conn.Write([]byte("GET / HTTP/1.1\r\nHost: target\r\n\r\n"))
buf := make([]byte, 4096)
n, _ := conn.Read(buf)
fmt.Println(string(buf[:n]))
}
// TCP server
func tcpServer() {
ln, _ := net.Listen("tcp", "0.0.0.0:4444")
for {
conn, _ := ln.Accept()
go handleConn(conn) // Handle each connection in goroutine
}
}
// Port scanner with goroutines
func scanPort(host string, port int, results chan<- int) {
addr := fmt.Sprintf("%s:%d", host, port)
conn, err := net.DialTimeout("tcp", addr, 500*time.Millisecond)
if err == nil {
conn.Close()
results <- port
}
}
func main() {
results := make(chan int, 100)
for port := 1; port <= 65535; port++ {
go scanPort("10.10.10.5", port, results)
}
// Collect results with timeout
}
// Concurrent scanner with semaphore
func scanWithLimit() {
sem := make(chan struct{}, 100) // Max 100 concurrent
var wg sync.WaitGroup
for port := 1; port <= 65535; port++ {
wg.Add(1)
sem <- struct{}{} // Acquire semaphore
go func(p int) {
defer wg.Done()
defer func() { <-sem }() // Release
addr := fmt.Sprintf("10.10.10.5:%d", p)
conn, err := net.DialTimeout("tcp", addr, 500*time.Millisecond)
if err == nil {
fmt.Printf("Port %d open\n", p)
conn.Close()
}
}(port)
}
wg.Wait()
}
HTTP#
import "net/http"
import "io"
// GET request
resp, err := http.Get("http://target.com")
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Println(string(body))
// POST request
resp, err := http.Post("http://target.com/login",
"application/x-www-form-urlencoded",
strings.NewReader("user=admin&pass=password"))
// Custom client (timeouts, proxy, skip TLS)
import "crypto/tls"
client := &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
Proxy: http.ProxyURL(proxyURL),
},
}
req, _ := http.NewRequest("GET", "https://target.com", nil)
req.Header.Set("User-Agent", "Mozilla/5.0")
req.Header.Set("Authorization", "Bearer TOKEN")
resp, err := client.Do(req)
// HTTP server (for C2, callbacks, file hosting)
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
fmt.Printf("Received: %s\n", body)
w.Write([]byte("OK"))
})
http.ListenAndServe(":8080", nil)
CONCURRENCY (GOROUTINES)#
import "sync"
// Fan-out pattern (parallel tasks)
var wg sync.WaitGroup
targets := []string{"10.10.10.1", "10.10.10.2", "10.10.10.3"}
for _, target := range targets {
wg.Add(1)
go func(t string) {
defer wg.Done()
// Scan target
}(target)
}
wg.Wait()
// Worker pool pattern
func worker(jobs <-chan string, results chan<- string) {
for target := range jobs {
// Process target
results <- fmt.Sprintf("%s: done", target)
}
}
jobs := make(chan string, 100)
results := make(chan string, 100)
for w := 0; w < 10; w++ { // 10 workers
go worker(jobs, results)
}
FILE & CRYPTO#
import (
"crypto/md5"
"crypto/sha256"
"encoding/hex"
"os"
)
// File hash
data, _ := os.ReadFile("file.exe")
md5Hash := md5.Sum(data)
sha256Hash := sha256.Sum256(data)
fmt.Printf("MD5: %s\n", hex.EncodeToString(md5Hash[:]))
fmt.Printf("SHA256: %s\n", hex.EncodeToString(sha256Hash[:]))
// AES encryption
import "crypto/aes"
import "crypto/cipher"
import "crypto/rand"
key := make([]byte, 32) // AES-256
rand.Read(key)
block, _ := aes.NewCipher(key)
gcm, _ := cipher.NewGCM(block)
nonce := make([]byte, gcm.NonceSize())
rand.Read(nonce)
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
// XOR
func xor(data, key []byte) []byte {
out := make([]byte, len(data))
for i := range data {
out[i] = data[i] ^ key[i%len(key)]
}
return out
}
CLI ARGUMENTS#
// Standard library (flag package)
import "flag"
target := flag.String("target", "", "Target host")
port := flag.Int("port", 80, "Target port")
threads := flag.Int("threads", 10, "Concurrent threads")
flag.Parse()
// Or use cobra for complex CLIs
// go get github.com/spf13/cobra
OS / EXEC#
import "os/exec"
// Run command
out, err := exec.Command("whoami").Output()
fmt.Println(string(out))
// Run with arguments
cmd := exec.Command("nmap", "-sV", "10.10.10.5")
cmd.Stdout = os.Stdout
cmd.Run()
// Run shell command
cmd := exec.Command("sh", "-c", "cat /etc/passwd | grep root")
out, _ := cmd.Output()
USEFUL LIBRARIES#
net/http # HTTP client/server (stdlib) crypto/* # Crypto (stdlib) encoding/json # JSON parsing (stdlib) os/exec # Command execution (stdlib) github.com/spf13/cobra # CLI framework github.com/projectdiscovery/goflags # PD-style flags github.com/go-resty/resty # HTTP client (easier) github.com/gorilla/websocket # WebSocket golang.org/x/crypto # Extended crypto (SSH, etc.) golang.org/x/net # Extended networking github.com/fatih/color # Terminal colors
EXISTING GO SECURITY TOOLS#
Nuclei # Template vulnerability scanner Subfinder # Subdomain discovery httpx # HTTP probing Katana # Web crawler Gobuster # Directory/DNS brute force Sliver # C2 framework Chisel # TCP/UDP tunnel Ligolo-ng # Tunneling/pivoting Garble # Go binary obfuscation Merlin # C2 framework
TIPS#
- CGO_ENABLED=0 for truly static binaries - -ldflags="-s -w" strips debug info (smaller binary) - Goroutines are cheap — spawn thousands for scanning - Use sync.WaitGroup to wait for goroutine completion - Channels for safe communication between goroutines - Cross-compile with GOOS and GOARCH env vars - Go binaries are larger than C/Rust (~5-15MB) - Use garble to obfuscate Go binaries for offensive use - Strong stdlib means fewer external dependencies - go install tool@latest installs Go tools instantly