Hello, fellow hackers, pen testers, and red team members! Welcome to another installment of “Programming Thursdays.” Today, we’re going to dive into the Go programming language. It’s a language that has been gaining popularity in recent years, and for good reason. Its fantastic features make it a strong contender for your next project or even a new tool in your pen-testing arsenal.

This comprehensive article will cover the basics of the Go programming language, including its syntax and basic concepts like variables, data types, operators, control structures, and functions. Then, we’ll get into the nitty-gritty of how to use Go in pen testing and red teaming, with plenty of code examples to help you get started. Finally, we’ll discuss the pros and cons of Go compared to other languages, specifically for our pen testing and red team community.

So buckle up, and let’s get started!

Go: A Brief Introduction

Go, often called Golang, is an open-source programming language developed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. It was first released in 2009, and since then, it has gained significant traction in the software development community. Go is a statically typed, compiled language, which means it offers both the safety of static typing and the speed of compiled code. It’s designed to be simple, efficient, and easy to read, making it an excellent choice for beginners and seasoned programmers.

One of Go’s main selling points is its powerful built-in concurrency support. Concurrency is a critical aspect of modern software development, as it allows programs to execute multiple tasks simultaneously, making the most of today’s multi-core processors. Go achieves this through Goroutines and channels, which we’ll discuss later in this article.

Go has a thriving community and a growing ecosystem of libraries and tools, making it an excellent choice for various applications, including web development, networking, and pen testing and red teaming.

So, without further ado, let’s dive into Go’s basic concepts and syntax.

Variables and Data Types

In Go, variables store values, and each variable has a specific data type. Let’s explore the different data types and how to declare and use variables in Go.

Declaring Variables

In Go, you can declare a variable using the var keyword, followed by the variable name, the data type, and an optional assignment to a value. Here’s an example:

var age int

In this example, we declare a variable named age of type int (integer). You can also declare a variable and assign a value in the same statement:

var age int = 30

Go also allows you to use type inference, which means you can omit the data type and let the compiler infer it based on the assigned value:

var age = 30 // inferred type: int

Finally, you can use the short variable declaration syntax, which combines the declaration and assignment of a value in one statement, using the := operator:

age := 30 // inferred type: int

Remember that the short variable declaration syntax can only be used inside functions.

Data Types

Go has a variety of built-in data types, including:

  • Numeric types:
    • Integer types (signed and unsigned): int8, int16, int32, int64, uint8, uint16, uint32, uint64
    • Special integer types: int, uint, uintptr
    • Floating-point types: float32, float64
    • Complex numbers: complex64, complex128
  • String type: string
  • Boolean type: bool

Let’s see some examples of declaring variables with different data types:

var name string = "Alice"
var score float64 = 97.5
var isStudent bool = true

Go also has derived data types, such as arrays, slices, structs, pointers, and maps. We’ll discuss some of these in more detail later in this article.

Operators

Operators are symbols that perform operations on values and variables. Go has several types of operators, including:

  • Arithmetic operators: +, -, *, /, %
  • Comparison operators: ==, !=, <, >, <=, >=
  • Logical operators: &&, ||, !
  • Bitwise operators: &, |, ^, &^, <<, >>
  • Assignment operators: =, +=,-=, *=, /=, %=, &=, |=, ^=, <<=, >>=
  • Miscellaneous operators: &, *, <-, ++, --

Let’s explore some examples of using operators in Go:

Arithmetic Operators

a := 10
b := 3
sum := a + b        // 13
difference := a - b // 7
product := a * b    // 30
quotient := a / b   // 3
remainder := a % b  // 1

Comparison Operators

x := 42
y := 13

isEqual := x == y // false
isNotEqual := x != y // true
isLess := x < y // false
isGreater := x > y // true
isLessOrEqual := x <= y // false
isGreaterOrEqual := x >= y // true

Logical Operators

isActive := true
isAdmin := false

isBoth := isActive && isAdmin // false
isEither := isActive || isAdmin // true
isNotAdmin := !isAdmin // true

Bitwise Operators

m := 6 // binary: 110
n := 4 // binary: 100

and := m & n // binary: 100, decimal: 4
or := m | n // binary: 110, decimal: 6
xor := m ^ n // binary: 010, decimal: 2
not := m &^ n // binary: 010, decimal: 2
leftShift := m << 1 // binary: 1100, decimal: 12
rightShift := m >> 1 // binary: 011, decimal: 3

Assignment Operators

x := 10
x += 5 // x = x + 5 => x = 15
x -= 3 // x = x - 3 => x = 12
x *= 2 // x = x * 2 => x = 24
x /= 4 // x = x / 4 => x = 6
x %= 5 // x = x % 5 => x = 1

Miscellaneous Operators

x := 5
ptr := &x // ptr is a pointer to the memory address of x

value := *ptr // value is 5, dereferencing the pointer to get the value of x

counter := 0
counter++ // increment counter by 1, counter = 1
counter-- // decrement counter by 1, counter = 0

Control Structures

Control structures are used to control the flow of execution in a program. Go has several control structures, including if, if-else, if-else if-else, switch, for, and select. Let’s explore some examples:

If Statement

score := 85

if score >= 90 {
    fmt.Println("Grade: A")
}

If-Else Statement

score := 75

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

If-Else If-Else Statement

score := 65

if score >= 90 {
    fmt.Println("Grade: A")
} else if score >= 80 {
    fmt.Println("Grade: B")
} else if score >= 70 {
    fmt.Println("Grade: C")
} else {
    fmt.Println("Grade: D")
}

Switch Statement

day := "Thursday"

switch day {
case "Monday":
    fmt.Println("It's Monday!")
case "Tuesday":
    fmt.Println("It's Tuesday!")
case "Wednesday":
    fmt.Println("It's Wednesday!")
case "Thursday":
    fmt.Println("It's Thursday!")
case "Friday":
    fmt.Println("It's Friday!")
default:
    fmt.Println("It's the weekend!")
}

For Loop

// Traditional for loop
for i := 0; i < 5; i++ {
    fmt.Println("Iteration:", i)
}

// While loop equivalent
counter := 0
for counter < 5 {
    fmt.Println("Counter:", counter)
    counter++
}

// Infinite loop
for {
    fmt.Println("This will run forever!")
    break // use break statement to exit the loop
}

Range Loop

names := []string{"Alice", "Bob", "Charlie"}

for index, name := range names {
    fmt.Printf("Name at index %d is %s\n", index, name)
}

for _, name := range names {
    fmt.Println("Name:", name)
}

Select Statement

timeout := make(chan bool, 1)
data := make(chan string, 1)

go func() {
    time.Sleep(2 * time.Second)
    timeout <- true
}()

go func() {
    data <- "Hello, World!"
}()

select {
case message := <-data:
    fmt.Println("Received:", message)
case <-timeout:
    fmt.Println("Timed out")
}

Functions

Functions are reusable blocks of code that can be called by name, accept input parameters, and return a value. In Go, functions are first-class citizens, which means they can be assigned to variables, passed as arguments and returned from other functions.

Defining and Calling Functions

To define a function, use the func keyword, followed by the function name, a list of input parameters in parentheses, the return type, and a code block. Here’s an example of a simple function that calculates the sum of two integers:

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

result := add(5, 3) // result is 8

You can also use named return values, which act as variables inside the function body:

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

result := add(5, 3) // result is 8

Variadic Functions

Go supports variadic functions, which can accept a variable number of input parameters. Use the ... syntax to indicate a variadic parameter:

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

result := sum(1, 2, 3, 4, 5) // result is 15

Higher-Order Functions

As mentioned earlier, functions are first-class citizens in Go, which means you can use them as input parameters or return values of other functions. Here’s an example of a higher-order function that accepts another function as an input parameter and applies it to two numbers:

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

func multiply(a int, b int) int {
    return a * b
}

result := apply(multiply, 5, 3) // result is 15

Go for Pen Testing and Red Teaming

Now that we’ve covered the basics of the Go programming language, let’s explore how it can be used for pen testing and red teaming. Go’s simplicity, efficiency, and strong concurrency support make it a great choice for building custom tools, exploiting vulnerabilities, and automating tasks.

Building a Port Scanner

As a first example, let’s build a simple port scanner using Go. We’ll use Go’s net package to establish TCP connections to a target IP address and check if specific ports are open.

package main

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

func main() {
    target := "192.168.1.1"
    startPort := 1
    endPort := 1024
    timeout := time.Second

    for port := startPort; port <= endPort; port++ {
        address := fmt.Sprintf("%s:%d", target, port)
        conn, err := net.DialTimeout("tcp", address, timeout)

        if err == nil {
            fmt.Printf("Port %d is open\n", port)
            conn.Close()
        }
    }
}

In this example, we loop through a range of port numbers and attempt to establish a TCP connection with a target IP address using net.DialTimeout. If the connection is successful, we print the open port number and close the connection.

Crafting Custom HTTP Requests

Crafting custom HTTP requests is another common task in pen testing and red teaming. Go’s net/http package provides a robust API for working with HTTP clients and servers. Let’s see an example of how to send an HTTP request with custom headers and a request body:

package main

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

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

    req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
    if err != nil {
        fmt.Println("Error creating request:", err)
        return
    }
    for key, value := range headers {
    req.Header.Set(key, value)
    }

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Error sending request:", err)
        return
    }
    defer resp.Body.Close()

    responseBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("Error reading response body:", err)
        return
    }

    fmt.Println("Response status:", resp.Status)
    fmt.Println("Response body:", string(responseBody))
}

In this example, we create an HTTP POST request with custom headers and a JSON request body. We then send the request using an http.Client and read the response status and body.

Brute Forcing Passwords

Go’s concurrency support makes it an excellent choice for tasks that require parallelism, such as brute-forcing passwords. Let’s create a simple example that uses Goroutines to attempt multiple password guesses simultaneously:

package main

import (
    "fmt"
    "sync"
)

func main() {
    passwordList := []string{"password", "123456", "qwerty", "P@ssw0rd"}
    targetPassword := "P@ssw0rd"
    var wg sync.WaitGroup

    for _, password := range passwordList {
        wg.Add(1)
        go func(password string) {
            defer wg.Done()

            if password == targetPassword
            {
            fmt.Println("Password found:", password)
            }
        }(password)
    }

    wg.Wait()
}

In this example, we use a sync.WaitGroup to keep track of Goroutines and ensure the program doesn’t exit before all Goroutines have finished. We then create a Goroutine for each password in the list and check if it matches the target password. When a match is found, we print the password.

Pros and Cons of Go for Pen Testers and Red Team Members

This section will discuss the pros and cons of using Go for pen testing and red teaming compared to other programming languages.

Pros

  1. Performance: Go’s compiled binaries offer excellent performance, which can be beneficial for tasks that require high-speed execution or a large number of concurrent operations.
  2. Concurrency: Go’s Goroutines and channels make writing concurrent and parallel code easy, making it well-suited for tasks like brute-forcing or port scanning.
  3. Static binaries: Go compiles to static binaries, which means you don’t need to install dependencies or runtime libraries on the target system. This makes it easy to deploy your tools on various systems.
  4. Cross-compilation: Go supports cross-compilation, allowing you to build binaries for different platforms from your development machine. This is useful when targeting various operating systems and architectures.
  5. Simplicity: Go has a simple syntax and a small standard library, which makes it easy to learn and write code quickly.
  6. Strong standard library: Go has a robust standard library that includes packages for working with networking, cryptography, file I/O, and more, which can be very useful when building custom tools for pen testing and red teaming.
  7. Growing ecosystem: The Go ecosystem is growing rapidly, with many libraries and tools available for pen testing and red teaming tasks.

Cons

  1. Less mature ecosystem: While the Go ecosystem is growing, it is still less mature than other languages like Python or Ruby, which have many libraries and tools available for pen testing and red teaming.
  2. Less widespread adoption: While Go is gaining popularity, it is still not as widely adopted as other languages like Python or Java. This might limit the availability of pre-built tools and resources.
  3. Verbose error handling: Go’s error handling can be verbose, requiring explicit error checks for most functions that can return an error. This can lead to more boilerplate code than languages with exception handling, like Python or Java.
  4. Lack of stealth: Go’s static binaries can be larger than equivalent tools written in other languages, which might make them more noticeable on a target system.

The Go programming language offers several advantages for pen testers and red team members, such as high performance, strong concurrency support, and cross-compilation capabilities. While its ecosystem is less mature than other languages, it grows rapidly and offers a solid foundation for building custom tools and exploits.

Conclusion

This article introduces the basics of the Go programming language, including its syntax, data types, operators, control structures, and functions. While Go may not be as widely adopted as Python or Java, it offers a unique combination of simplicity, performance, and concurrency, making it well-suited for many pen testing and red teaming tasks. By learning Go and integrating it into your toolbox, you can expand your skill set, build custom tools, and improve the effectiveness of your engagements.

So, consider going Go next time you’re gearing up for a pen testing or red team assignment. You may find that its simplicity and power provide just the edge you need to accomplish your objectives and stay ahead in the ever-evolving world of cybersecurity.

Remember that the key to success as a pen tester or red team member is continually learning and adapting to new challenges. By exploring different programming languages and tools, you can broaden your expertise and become an even more effective and versatile professional.

Happy hacking, and remember to keep honing your skills, exploring new technologies, and pushing the boundaries of what’s possible with Go and other languages in the fascinating field of pen testing and red teaming!