Go for Cyber Security
Build blazingly fast security tools with Go - concurrent port scanners, web crawlers, and network analysis tools with built-in concurrency.
Why Go for Security
Go (Golang) is the language of modern security tooling. Tools like Nuclei, Subfinder, httpx, Naabu, and Amass are all written in Go, making it essential for security professionals.
- Blazing fast - Compiled to native binary, no interpreter overhead.
- Built-in concurrency - Goroutines and channels make parallel scanning trivial.
- Single binary - Compile to one executable, deploy anywhere.
- Strong standard library - Networking, crypto, HTTP built-in.
- Industry tools - Nuclei, Subfinder, httpx, Naabu, Amass are all Go.
Go Basics
go
package main
import (
"fmt"
"net"
"time"
)
// Struct for scan results
type ScanResult struct {
Port int
Open bool
Service string
Banner string
}
// Map for service lookup
var services = map[int]string{
22: "SSH", 80: "HTTP", 443: "HTTPS",
3306: "MySQL", 8080: "HTTP-Alt",
}
func checkPort(host string, port int) ScanResult {
addr := fmt.Sprintf("%s:%d", host, port)
conn, err := net.DialTimeout("tcp", addr, 1*time.Second)
if err != nil {
return ScanResult{Port: port, Open: false}
}
conn.Close()
svc := services[port]
if svc == "" { svc = "Unknown" }
return ScanResult{Port: port, Open: true, Service: svc}
}
func main() {
target := "192.168.1.1"
for port := 1; port <= 1024; port++ {
result := checkPort(target, port)
if result.Open {
fmt.Printf("[+] Port %d: %s\n", result.Port, result.Service)
}
}
}
Goroutines & Channels
go
package main
import (
"fmt"
"net"
"sync"
"time"
)
// Channel for collecting results
func concurrentScan(host string, start, end int, maxWorkers int) []ScanResult {
results := []ScanResult{}
var mu sync.Mutex
var wg sync.WaitGroup
// Worker pool pattern
sem := make(chan struct{}, maxWorkers)
for port := start; port <= end; port++ {
wg.Add(1)
go func(p int) {
defer wg.Done()
sem <- struct{}{} // Acquire slot
defer func() { <-sem }() // Release slot
result := checkPort(host, p)
if result.Open {
mu.Lock()
results = append(results, result)
mu.Unlock()
fmt.Printf("[+] Port %d: %s\n", result.Port, result.Service)
}
}(port)
}
wg.Wait()
return results
}
func main() {
start := time.Now()
results := concurrentScan("192.168.1.1", 1, 65535, 1000)
elapsed := time.Since(start)
fmt.Printf("\nScanned 65535 ports in %v\n", elapsed)
fmt.Printf("Found %d open ports\n", len(results))
}
Network Programming
go - banner_grab.go
package main
import (
"bufio"
"fmt"
"net"
"strings"
"time"
)
func grabBanner(host string, port int) (string, error) {
addr := fmt.Sprintf("%s:%d", host, port)
conn, err := net.DialTimeout("tcp", addr, 5*time.Second)
if err != nil {
return "", err
}
defer conn.Close()
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
// Send HTTP request to grab banner
fmt.Fprintf(conn, "HEAD / HTTP/1.1\r\nHost: %s\r\n\r\n", host)
reader := bufio.NewReader(conn)
line, _ := reader.ReadString('\n')
return strings.TrimSpace(line), nil
}
func main() {
ports := []int{21, 22, 25, 80, 443}
for _, port := range ports {
banner, err := grabBanner("192.168.1.1", port)
if err == nil && banner != "" {
fmt.Printf("Port %d: %s\n", port, banner)
}
}
}
Fast Port Scanner
go - scanner/main.go
package main
import (
"encoding/json"
"flag"
"fmt"
"net"
"os"
"sync"
"time"
)
type Result struct {
Port int `json:"port"`
State string `json:"state"`
Service string `json:"service"`
}
func scan(target string, port int, timeout time.Duration) Result {
addr := fmt.Sprintf("%s:%d", target, port)
conn, err := net.DialTimeout("tcp", addr, timeout)
if err != nil {
return Result{Port: port, State: "closed"}
}
conn.Close()
return Result{Port: port, State: "open"}
}
func main() {
target := flag.String("target", "127.0.0.1", "Target host")
workers := flag.Int("workers", 1000, "Concurrent workers")
flag.Parse()
var results []Result
var mu sync.Mutex
var wg sync.WaitGroup
sem := make(chan struct{}, *workers)
start := time.Now()
fmt.Printf("[*] Scanning %s with %d workers\n", *target, *workers)
for port := 1; port <= 65535; port++ {
wg.Add(1)
go func(p int) {
defer wg.Done()
sem <- struct{}{}
defer func() { <-sem }()
r := scan(*target, p, 1*time.Second)
if r.State == "open" {
mu.Lock()
results = append(results, r)
mu.Unlock()
fmt.Printf("[+] Port %d OPEN\n", p)
}
}(port)
}
wg.Wait()
elapsed := time.Since(start)
// Save JSON report
data, _ := json.MarshalIndent(results, "", " ")
os.WriteFile("scan_results.json", data, 0644)
fmt.Printf("\n[*] Complete: %d open ports in %v\n", len(results), elapsed)
}
Web Crawler
go - crawler.go
package main
import (
"fmt"
"io"
"net/http"
"regexp"
"strings"
)
func crawl(url string) []string {
resp, err := http.Get(url)
if err != nil {
return nil
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
content := string(body)
// Extract links
re := regexp.MustCompile(`href="([^"]*)"`)
matches := re.FindAllStringSubmatch(content, -1)
var links []string
for _, m := range matches {
link := m[1]
if strings.HasPrefix(link, "http") || strings.HasPrefix(link, "/") {
links = append(links, link)
}
}
return links
}
func main() {
links := crawl("http://example.com")
fmt.Printf("Found %d links:\n", len(links))
for _, l := range links {
fmt.Println(l)
}
}
Practice
Study the source code of popular Go security tools: Nuclei (projectdiscovery/nuclei), Subfinder, httpx, and Naabu on GitHub.
Resources
- Go Documentation: go.dev/doc
- Go by Example: gobyexample.com
- ProjectDiscovery: github.com/projectdiscovery
- Black Hat Go: Tom Steele, Chris Patten, Dan Kottmann
- Network Programming with Go: Adam Woodbeck