Hello, fellow hackers, pen testers, and red team members! Welcome to another installment of “Programming Thursdays.” Today, we’re diving into the Go programming language—a rising favorite among security pros. Go’s unique combination of speed, simplicity, and concurrency support makes it a compelling choice for building custom red team tooling and automation.

This post walks through Go’s syntax and core concepts—variables, data types, operators, control structures, and functions—before shifting into real-world usage with code examples for port scanning, HTTP manipulation, and brute-force attacks. We’ll wrap up with a discussion of pros and cons specific to offensive security work.

Let’s get to it.

Go: A Brief Introduction

Go (often called Golang) is an open-source language developed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. Released in 2009, it’s known for being statically typed, compiled, and extremely fast to build and execute. Its syntax is clean and minimal, reducing mental overhead.

One of Go’s biggest strengths is its built-in concurrency model, powered by Goroutines and channels. Unlike threads, Goroutines are lightweight and efficient, allowing you to perform thousands of concurrent operations with minimal overhead. While concurrency refers to managing many tasks at once (not necessarily in parallel), Go’s runtime will leverage multiple CPU cores for parallelism when available.

Go’s toolchain and ecosystem are rock-solid—everything from formatting to dependency management is built in. For red teamers, it’s particularly nice that Go can generate small, statically linked binaries that “just work” on target systems (within reason).

_ Unless otherwise nnoted all code samples are written in Go 1.18._

Variables and Data Types

Declaring Variables

var age int          // declares an int
var score = 90       // type inferred as int
age := 30            // shorthand declaration (only works inside functions)

Data Types

Go supports:

  • Numeric types: int, float64, uint8, int32, etc.
  • Strings: string
  • Booleans: bool
  • Composite types: arrays, slices, maps, structs, pointers
var name string = "Alice"
var score float64 = 97.5
var isStudent bool = true

Operators

Here’s a sample of Go’s operators in action:

Arithmetic

a, b := 10, 3
fmt.Println(a+b, a-b, a*b, a/b, a%b)

Comparison

x, y := 42, 13
fmt.Println(x == y, x != y, x > y)

Logical

active, admin := true, false
fmt.Println(active && admin, active || admin, !admin)

Bitwise

m, n := 6, 4
fmt.Println(m & n)   // AND
fmt.Println(m | n)   // OR
fmt.Println(m ^ n)   // XOR
fmt.Println(m &^ n)  // AND NOT

Miscellaneous

x := 5
ptr := &x      // pointer
fmt.Println(*ptr) // dereference

Control Structures

If / Else / Switch

if score >= 90 {
    fmt.Println("Grade: A")
} else if score >= 80 {
    fmt.Println("Grade: B")
} else {
    fmt.Println("Needs work")
}

switch day := "Thursday"; day {
case "Monday":
    fmt.Println("Ugh.")
case "Friday":
    fmt.Println("Finally!")
default:
    fmt.Println("Just another day.")
}

For Loops

for i := 0; i < 5; i++ {
    fmt.Println(i)
}

names := []string{"Alice", "Bob", "Charlie"}
for index, name := range names {
    fmt.Println(index, name)
}

Select Statement

Useful when working with multiple channels (e.g., network tools).

select {
case msg := <-data:
    fmt.Println("Received:", msg)
case <-timeout:
    fmt.Println("Timeout reached")
}

Functions

Basic

func add(x int, y int) int {
    return x + y
}

Named Returns

func add(x int, y int) (sum int) {
    sum = x + y
    return // sum is returned implicitly
}

Variadic

func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

Higher-Order

func apply(f func(int, int) int, x, y int) int {
    return f(x, y)
}

Go for Pen Testing and Red Teaming

Here’s where Go gets spicy for offensive use cases.

Port Scanning

🚨 Note: Always have explicit permission before scanning systems.

package main

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

func main() {
    target := "192.168.1.1"
    for port := 1; port <= 1024; port++ {
        address := fmt.Sprintf("%s:%d", target, port)
        conn, err := net.DialTimeout("tcp", address, 1*time.Second)
        if err == nil {
            fmt.Printf("Port %d is open\n", port)
            conn.Close()
        }
    }
}

Custom HTTP Requests

package main

import (
    "bytes"
    "fmt"
    "io"
    "net/http"
)

func main() {
    url := "https://example.com/login"
    headers := map[string]string{
        "User-Agent": "RedTeamTool",
    }
    body := []byte(`{"username": "admin", "password": "P@ssw0rd"}`)

    req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
    if err != nil {
        panic(err)
    }

    for k, v := range headers {
        req.Header.Set(k, v)
    }

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    respBody, err := io.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }

    fmt.Println("Status:", resp.Status)
    fmt.Println("Body:", string(respBody))
}

Brute Forcing

⚠️ This is for demonstration only. Don’t use irresponsibly.

package main

import (
    "fmt"
    "sync"
)

func main() {
    passwords := []string{"123456", "password", "letmein", "P@ssw0rd"}
    target := "P@ssw0rd"
    var wg sync.WaitGroup

    for _, pwd := range passwords {
        wg.Add(1)
        go func(p string) {
            defer wg.Done()
            if p == target {
                fmt.Println("Match found:", p)
            }
        }(pwd)
    }

    wg.Wait()
}

Pros and Cons for Red Teamers

Pros

  • Fast binaries: Useful for evading time-based detection.
  • Concurrency: Excellent for tasks like scanning and guessing.
  • Cross-compilation: Build once, run anywhere (Linux, Windows, etc.).
  • Static linking: Portable, self-contained binaries.
  • Minimal runtime: Small footprint on disk and in memory.
  • Good standard lib: HTTP, crypto, sockets—all built in.

Cons

  • Verbose error handling: if err != nil everywhere.
  • Bigger binaries: Statically compiled tools can stand out.
  • Limited stealth: No built-in obfuscation or packers.
  • Fewer libraries: Especially compared to Python’s infosec tools.
  • Not ideal for scripting: No REPL, not interactive.

Conclusion

Go is fast, portable, and effective—an ideal language for building red team tools. Whether you’re scanning, automating, or crafting payloads, its strengths in concurrency and performance shine.

Is it the best fit for every task? Nope. But for compiled tooling and headless agents, it’s a rock-solid choice.

So next time you’re building something sneaky, consider going Go.

Happy hacking, and see you next Programming Thursday!