mirror of
https://github.com/jessfraz/dockerfiles.git
synced 2025-05-05 18:14:40 +02:00
use masscan for k8scan
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
parent
cabc5dad9c
commit
cf4dd75b5c
162
k8scan/main.go
162
k8scan/main.go
@ -11,7 +11,9 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -37,8 +39,11 @@ var (
|
|||||||
cidr string
|
cidr string
|
||||||
|
|
||||||
defaultPorts = intSlice{80, 443, 8001, 9001}
|
defaultPorts = intSlice{80, 443, 8001, 9001}
|
||||||
|
originalPorts string
|
||||||
ports intSlice
|
ports intSlice
|
||||||
|
|
||||||
|
useMasscan bool
|
||||||
|
|
||||||
mailgunDomain string
|
mailgunDomain string
|
||||||
mailgunAPIKey string
|
mailgunAPIKey string
|
||||||
emailRecipient string
|
emailRecipient string
|
||||||
@ -62,6 +67,8 @@ func (i *intSlice) String() (out string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *intSlice) Set(value string) error {
|
func (i *intSlice) Set(value string) error {
|
||||||
|
originalPorts = value
|
||||||
|
|
||||||
// Set the default if nothing was given.
|
// Set the default if nothing was given.
|
||||||
if len(value) <= 0 {
|
if len(value) <= 0 {
|
||||||
*i = defaultPorts
|
*i = defaultPorts
|
||||||
@ -110,6 +117,8 @@ func init() {
|
|||||||
flag.StringVar(&cidr, "cidr", defaultCIDR, "IP CIDR to scan")
|
flag.StringVar(&cidr, "cidr", defaultCIDR, "IP CIDR to scan")
|
||||||
flag.Var(&ports, "ports", fmt.Sprintf("Ports to scan (ex. 80-443 or 80,443,8080 or 1-20,22,80-443) (default %q)", defaultPorts.String()))
|
flag.Var(&ports, "ports", fmt.Sprintf("Ports to scan (ex. 80-443 or 80,443,8080 or 1-20,22,80-443) (default %q)", defaultPorts.String()))
|
||||||
|
|
||||||
|
flag.BoolVar(&useMasscan, "masscan", true, "Use masscan binary for scanning (this is faster than using pure golang)")
|
||||||
|
|
||||||
flag.StringVar(&mailgunAPIKey, "mailgun-api-key", "", "Mailgun API Key to use for sending email (optional)")
|
flag.StringVar(&mailgunAPIKey, "mailgun-api-key", "", "Mailgun API Key to use for sending email (optional)")
|
||||||
flag.StringVar(&mailgunDomain, "mailgun-domain", "", "Mailgun Domain to use for sending email (optional)")
|
flag.StringVar(&mailgunDomain, "mailgun-domain", "", "Mailgun Domain to use for sending email (optional)")
|
||||||
flag.StringVar(&emailRecipient, "email-recipient", "", "Recipient for email notifications (optional)")
|
flag.StringVar(&emailRecipient, "email-recipient", "", "Recipient for email notifications (optional)")
|
||||||
@ -122,10 +131,15 @@ func init() {
|
|||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// set log level
|
// Set the log level.
|
||||||
if debug {
|
if debug {
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the default ports.
|
||||||
|
if len(ports) <= 0 {
|
||||||
|
ports = defaultPorts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -145,31 +159,58 @@ func main() {
|
|||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
log.SetOutput(ioutil.Discard)
|
log.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
logrus.Infof("Scanning for Kubernetes Dashboards and API Servers on %s over port range %#v", cidr, ports)
|
logrus.Infof("Scanning for Kubernetes Dashboards and API Servers on %s over port range %s", cidr, originalPorts)
|
||||||
if len(mailgunDomain) > 0 && len(mailgunAPIKey) > 0 && len(emailRecipient) > 0 {
|
if len(mailgunDomain) > 0 && len(mailgunAPIKey) > 0 && len(emailRecipient) > 0 {
|
||||||
logrus.Infof("Using Mailgun Domain %s, API Key %s to send emails to %s", mailgunDomain, mailgunAPIKey, emailRecipient)
|
logrus.Infof("Using Mailgun Domain %s, API Key %s to send emails to %s", mailgunDomain, mailgunAPIKey, emailRecipient)
|
||||||
}
|
}
|
||||||
logrus.Infof("This may take a bit...")
|
logrus.Infof("This may take a bit...")
|
||||||
|
|
||||||
startTime := time.Now()
|
var (
|
||||||
|
startTime = time.Now()
|
||||||
|
wg sync.WaitGroup
|
||||||
|
)
|
||||||
|
|
||||||
ip, ipnet, err := net.ParseCIDR(cidr)
|
if useMasscan {
|
||||||
|
m, err := doMasscan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
for _, result := range m {
|
||||||
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
|
for _, port := range result.Ports {
|
||||||
for _, port := range ports {
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(ip string, port int) {
|
go func(ip string, port int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
scanIP(ip, port)
|
scanIP(ip, port)
|
||||||
|
|
||||||
|
}(result.IP, port.Port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ip, ipnet, err := net.ParseCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
|
||||||
|
for _, port := range ports {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(ip string, port int) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
// Check if the port is open.
|
||||||
|
ok := portOpen(ip, port)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
scanIP(ip, port)
|
||||||
|
|
||||||
}(ip.String(), port)
|
}(ip.String(), port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
@ -178,12 +219,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func scanIP(ip string, port int) {
|
func scanIP(ip string, port int) {
|
||||||
// Check if the port is open.
|
|
||||||
ok := portOpen(ip, port)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it's a kubernetes dashboard.
|
// Check if it's a kubernetes dashboard.
|
||||||
ok, uri := isKubernetesDashboard(ip, port)
|
ok, uri := isKubernetesDashboard(ip, port)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -286,6 +321,58 @@ type OrganizationJSON struct {
|
|||||||
Reference string `json:"$,omitempty"`
|
Reference string `json:"$,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MasscanResult holds the masscan results data struct.
|
||||||
|
// Looks like:
|
||||||
|
// [
|
||||||
|
// {
|
||||||
|
// "ip": "104.198.238.41",
|
||||||
|
// "timestamp": "1531524211",
|
||||||
|
// "ports": [
|
||||||
|
// {
|
||||||
|
// "port": 22,
|
||||||
|
// "proto": "tcp",
|
||||||
|
// "status": "open",
|
||||||
|
// "reason": "syn-ack",
|
||||||
|
// "ttl": 56
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// },
|
||||||
|
// ...
|
||||||
|
// ]
|
||||||
|
type MasscanResult struct {
|
||||||
|
IP string `json:"ip,omitempty"`
|
||||||
|
Timestamp MasscanTime `json:"timestamp,omitempty"`
|
||||||
|
Ports []MasscanPort `json:"ports,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MasscanPort defines the data struct for a masscan port.
|
||||||
|
type MasscanPort struct {
|
||||||
|
Port int `json:"port,omitempty"`
|
||||||
|
Protocol string `json:"proto,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Reason string `json:"reason,omitempty"`
|
||||||
|
TTL int `json:"ttl,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MasscanTime is the time format returned by masscan.
|
||||||
|
type MasscanTime struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON sets MasscanTime correctly from a string.
|
||||||
|
func (t *MasscanTime) UnmarshalJSON(b []byte) error {
|
||||||
|
s := strings.Trim(strings.TrimSpace(string(b)), `"`)
|
||||||
|
|
||||||
|
i, err := strconv.ParseInt(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*t = MasscanTime{time.Unix(i, 0)}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getIPInfo(ip string) (b ARINResponse, err error) {
|
func getIPInfo(ip string) (b ARINResponse, err error) {
|
||||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf(arinAPIEndpoint, ip), nil)
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf(arinAPIEndpoint, ip), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -346,3 +433,52 @@ ARIN: %s
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func doMasscan() ([]MasscanResult, error) {
|
||||||
|
// Create a temporary directory for the output.
|
||||||
|
dir, err := ioutil.TempDir(os.TempDir(), "masscan")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("creating temporary directory failed: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
file := filepath.Join(dir, "scan.json")
|
||||||
|
|
||||||
|
cmd := exec.Command("masscan",
|
||||||
|
fmt.Sprintf("-p%s", ports.String()),
|
||||||
|
cidr,
|
||||||
|
"--output-format", "json",
|
||||||
|
"--output-file", file,
|
||||||
|
"--rate", "1000000",
|
||||||
|
"--exclude", "255.255.255.255",
|
||||||
|
)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
logrus.Infof("Running masscan command: `%s`", strings.Join(cmd.Args, " "))
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return nil, fmt.Errorf("running masscan failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := cleanMasscanOutputFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cleaning up masscan file failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m := []MasscanResult{}
|
||||||
|
if err := json.Unmarshal(b, &m); err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshal json failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("masscan result: %#v", m)
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanMasscanOutputFile(file string) ([]byte, error) {
|
||||||
|
b, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(strings.TrimSuffix(strings.TrimSpace(string(b)), ",\n]") + "]"), nil
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user