Intro To Go - A Naive TCP Port Scanner
Intro⌗
So over the weekend I picked up a copy of Black Hat Go by Tom Steele and have been following along. I have a little experience with Python and Bash, so Go wasn’t terribly hard to jump into but definitely different as I have zero experience with C or other similar languages.
So why choose Go to learn?
- It’s minimalist as hell (the language spec is only 50 pages)
- Concurrency is a big part of Go (unfortunately I won’t be utilizing that feature in this project)
- Go can cross-compile static binaries!
The Naive Approach
Whenever learning a new programming language or concept, I find the “naive” approach works well. Instead of trying to implement the “best” approach to a problem, simply implement the first one that comes to mind and build off of that. I like taking a project-oriented approach to learning so I decided to build a “naive” TCP port-scanner utilizing what I’ve learned so far with Go.
The Code⌗
The repo can be found here
package main
import (
"bufio"
"fmt"
"net"
"os"
)
func main() {
// open our file for reading
file, _ := os.Open("ports.conf")
// create a scanner object that reads the file we opened before
// by default, NewScanner splits on newlines
scanner := bufio.NewScanner(file)
// we need to initialize an empty slice here for text in order
// for the append function on line 25 to work
var text []string
// read in the file line by line, save it to "text" and close the file
for scanner.Scan() {
text = append(text, scanner.Text())
}
file.Close()
// os.Args[1] parses argv
TARGET := os.Args[1]
fmt.Printf("The target is %s", TARGET)
var openPorts []string
for _, port := range text {
fmt.Printf("\r\nCurrently scanning: %s:%s", TARGET, port)
address := TARGET + ":" + port
conn, err := net.Dial("tcp", address)
if err != nil {
//port is either filtered or closed
continue
}
conn.Close()
openPorts = append(openPorts, port)
fmt.Printf("\r\n[*]Port %s is open!\n", port)
}
fmt.Printf("\nThe ports open for %s are %s", TARGET, openPorts)
}
Breaking Down The Code⌗
file, _ := os.Open("ports.conf")
- ports.conf will be our config file, simply fill it up with newline separated ports we want to TCP scan
scanner := bufio.NewScanner(file)
var text []string
for scanner.Scan() {
text = append(text, scanner.Text())
}
file.Close()
- this bit reads our ports file and stores the result as an array in a variable called text
TARGET := os.Args[1]
fmt.Printf("The target is %s", TARGET)
var openPorts []string
- initialize our target as a constant that is read as a command line argument
- e.g
./scan scanme.nmap.org
- also declare a string array we’ll use to store open ports we find
for _, port := range text {
address := TARGET + ":" + port
conn, err := net.Dial("tcp", address)
if err != nil {
//port is either filtered or closed
continue
}
conn.Close()
openPorts = append(openPorts, port)
- finally, we get to the meaty part of the scanner, the actual scanning routine that utilizes the built-in
net
package that golang provides - “err” lets us know if the port is filtered or closed
- each open port we find gets appended to openPorts, the array we declared earlier
Conclusion⌗
This is a really bad and slow TCP port scanner, there’s many things I would improve upon like:
- making use of concurrency
- scan top 100 ports by default
- read multiple hosts
- different type of scans (half-open, UDP, Xmas, etc..)
- improving user UI
These features will come in the next iteration!