-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
316 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// Turns a string of ports separated by '-' or ',' and returns a slice of Ints. | ||
func explode(s string) ([]int, error) { | ||
const errmsg = "Invalid port specification" | ||
ports := []int{} | ||
switch { | ||
case strings.Contains(s, "-"): | ||
sp := strings.Split(s, "-") | ||
if len(sp) != 2 { | ||
return ports, errors.New(errmsg) | ||
} | ||
start, err := strconv.Atoi(sp[0]) | ||
if err != nil { | ||
return ports, errors.New(errmsg) | ||
} | ||
end, err := strconv.Atoi(sp[1]) | ||
if err != nil { | ||
return ports, errors.New(errmsg) | ||
} | ||
if start > end || start < 1 || end > 65535 { | ||
return ports, errors.New(errmsg) | ||
} | ||
for ; start <= end; start++ { | ||
ports = append(ports, start) | ||
} | ||
case strings.Contains(s, ","): | ||
sp := strings.Split(s, ",") | ||
for _, p := range sp { | ||
i, err := strconv.Atoi(p) | ||
if err != nil { | ||
return ports, errors.New(errmsg) | ||
} | ||
if i < 1 || i > 65535 { | ||
return ports, errors.New(errmsg) | ||
} | ||
ports = append(ports, i) | ||
} | ||
default: | ||
i, err := strconv.Atoi(s) | ||
if err != nil { | ||
return ports, errors.New(errmsg) | ||
} | ||
if i < 1 || i > 65535 { | ||
return ports, errors.New(errmsg) | ||
} | ||
ports = append(ports, i) | ||
} | ||
return ports, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"github.com/docopt/docopt.go" | ||
"github.com/miekg/pcap" | ||
"github.com/tomsteele/cookiescan/result" | ||
"log" | ||
"net" | ||
"os" | ||
"sort" | ||
"strconv" | ||
"text/tabwriter" | ||
"time" | ||
) | ||
|
||
type empty struct{} | ||
|
||
func main() { | ||
args, err := docopt.Parse(usage, nil, true, "cookiescan 0.1", false) | ||
if err != nil { | ||
log.Fatal("Error parsing usage. Error: ", err.Error()) | ||
} | ||
host := args["<target>"].(string) | ||
ports, err := explode(args["-p"].(string)) | ||
if err != nil { | ||
log.Fatal(err.Error()) | ||
} | ||
|
||
var ip string | ||
if net.ParseIP(host) == nil { | ||
ips, err := net.LookupIP(host) | ||
if err != nil { | ||
log.Fatal("Could not resolve hostname. Error: ", err.Error()) | ||
} | ||
ip = ips[0].String() | ||
} else { | ||
ip = host | ||
} | ||
|
||
minc, err := strconv.Atoi(args["-c"].(string)) | ||
if err != nil { | ||
log.Fatal("Invalid argument for -c.") | ||
} | ||
concurrency, err := strconv.Atoi(args["-g"].(string)) | ||
if err != nil { | ||
log.Fatal("Invalid argument for -g.") | ||
} | ||
ti, err := strconv.Atoi(args["-t"].(string)) | ||
if err != nil { | ||
log.Fatal("Invalid argument for -t.") | ||
} | ||
timeout := time.Duration(ti) * time.Millisecond | ||
|
||
filter := fmt.Sprintf("src %s and ((tcp[13] == 0x11) or (tcp[13] == 0x10) or (tcp[13] == 0x18))", ip) | ||
var device string | ||
if args["-i"] != nil { | ||
device = args["-i"].(string) | ||
} | ||
if device == "" { | ||
devs, err := pcap.FindAllDevs() | ||
if err != "" { | ||
log.Fatal("Error finding interfaces. Error: ", err) | ||
} | ||
if len(devs) == 0 { | ||
log.Fatal("No interfaces found. Are you not running as root?") | ||
} | ||
device = devs[0].Name | ||
} | ||
|
||
h, err := pcap.OpenLive(device, int32(320), true, 500) | ||
if err != nil { | ||
log.Fatal(err.Error()) | ||
} | ||
if err = h.SetFilter(filter); err != nil { | ||
log.Fatal(err.Error()) | ||
} | ||
|
||
res := make(map[uint16][]string) | ||
tasks := make(chan int, concurrency) | ||
track := make(chan empty) | ||
|
||
go func() { | ||
for pkt, r := h.NextEx(); r >= 0; pkt, r = h.NextEx() { | ||
select { | ||
case <-track: | ||
break | ||
default: | ||
if r == 0 { | ||
continue | ||
} | ||
pkt.Decode() | ||
t := pkt.Headers[1].(*pcap.Tcphdr) | ||
f := t.FlagsString() | ||
res[t.SrcPort] = append(res[t.SrcPort], f) | ||
} | ||
} | ||
}() | ||
|
||
for i := 0; i < concurrency; i++ { | ||
go func() { | ||
for p := range tasks { | ||
c, err := net.DialTimeout("tcp", ip+":"+strconv.Itoa(p), timeout) | ||
if err != nil { | ||
continue | ||
} | ||
c.Close() | ||
} | ||
}() | ||
} | ||
|
||
log.Printf("Staring scan of %s.\n", ip) | ||
for _, p := range ports { | ||
tasks <- p | ||
} | ||
close(tasks) | ||
time.Sleep(time.Duration(2 * time.Second)) | ||
track <- empty{} | ||
h.Close() | ||
log.Println("Scan complete.") | ||
|
||
services, _ := buildServices() | ||
results := cookiescan.Result{Host: ip} | ||
for k, v := range res { | ||
conf := len(v) | ||
if conf < minc { | ||
continue | ||
} | ||
service := "unknown" | ||
if s, ok := services[int(k)]; ok { | ||
service = s | ||
} | ||
p := cookiescan.Port{Port: int(k), Service: service, State: "open", Confidence: conf, Reason: v} | ||
results.Ports = append(results.Ports, p) | ||
} | ||
sort.Sort(results.Ports) | ||
|
||
if args["-j"].(bool) { | ||
j, _ := json.MarshalIndent(results, "", " ") | ||
fmt.Println(string(j)) | ||
} else { | ||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 4, ' ', 0) | ||
fmt.Fprintln(w, "Port\tState\tService\tConfidence\tReason") | ||
for _, p := range results.Ports { | ||
fmt.Fprintf(w, "%d\t%s\t%s\t%d\t%s\n", p.Port, p.State, p.Service, p.Confidence, p.Reason) | ||
} | ||
w.Flush() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package cookiescan | ||
|
||
type Result struct { | ||
Host string `json:"host"` | ||
Ports Ports `json:"ports"` | ||
} | ||
|
||
type Port struct { | ||
Port int `json:"port"` | ||
Service string `json:"service"` | ||
State string `json:"state"` | ||
Confidence int `json:"confidence"` | ||
Reason []string `json:"reason"` | ||
} | ||
|
||
type Ports []Port | ||
|
||
func (p Ports) Len() int { return len(p) } | ||
func (p Ports) Swap(i, j int) { p[i], p[j] = p[j], p[i] } | ||
func (p Ports) Less(i, j int) bool { return p[i].Port < p[j].Port } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"os" | ||
"regexp" | ||
"strconv" | ||
) | ||
|
||
// Reads '/etc/services' and creates a port[service] lookup table | ||
func buildServices() (map[int]string, error) { | ||
services := make(map[int]string) | ||
f, err := os.Open("/etc/services") | ||
if err != nil { | ||
return services, err | ||
} | ||
defer f.Close() | ||
scanner := bufio.NewScanner(f) | ||
re := regexp.MustCompile("([^\\s]+)\\s+([0-9]+)/(tcp)") | ||
for scanner.Scan() { | ||
result := re.FindStringSubmatch(scanner.Text()) | ||
if len(result) == 4 { | ||
port, err := strconv.Atoi(result[2]) | ||
if err != nil { | ||
continue | ||
} | ||
services[port] = result[1] | ||
} | ||
} | ||
if err := scanner.Err(); err != nil { | ||
return services, err | ||
} | ||
return services, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package main | ||
|
||
const usage = ` | ||
Usage: | ||
cookiescan [options] <target> | ||
cookiescan -h | --help | ||
cookiescan -v | --version | ||
Required Arguments: | ||
target: IP Address or Hostname | ||
Options: | ||
-h --help Show this message. | ||
-v --version Show version. | ||
-p <port ranges> Ex: -p 22; -p 1-65535, -p 80,443. [default: 1-1024] | ||
-g <int> Amount of goroutines to spread connection attempts across. [default: 1000] | ||
-c <int> Minimum confidence level to flag port as open. [default: 1] | ||
-i <interface> Network interface to listen on. | ||
-t <timeout> Timeout in Milliseconds to wait for a connection. [default: 400] | ||
-j Output JSON. | ||
` |