AETH-erial 10 kuukautta sitten
vanhempi
sitoutus
e633c1c5b9
6 muutettua tiedostoa jossa 158 lisäystä ja 119 poistoa
  1. 19 19
      cmd/kyoketsu/kyoketsu.go
  2. 13 17
      pkg/local.go
  3. 2 4
      pkg/local_test.go
  4. 77 54
      pkg/scanner.go
  5. 21 11
      pkg/scanner_test.go
  6. 26 14
      pkg/webserver.go

+ 19 - 19
cmd/kyoketsu/kyoketsu.go

@@ -31,10 +31,7 @@ import (
 	"flag"
 	"fmt"
 	"log"
-	"net"
 	"os"
-	"strings"
-	"sync"
 
 	kyoketsu "git.aetherial.dev/aeth/kyoketsu/pkg"
 	"github.com/manifoldco/promptui"
@@ -75,29 +72,32 @@ func main() {
 	if err != nil {
 		log.Fatal(err)
 	}
-	var addr *kyoketsu.IpSubnetMapper
+	var addr kyoketsu.IpSubnetMapper
 	targetNet := fmt.Sprintf("%s/%s", localAddr.Choice[choice].HostAddress, localAddr.Choice[choice].Cidr)
 	addr, err = kyoketsu.GetNetworkAddresses(targetNet)
 	if err != nil {
 		log.Fatal(err)
 	}
-	var wg sync.WaitGroup
-	for i := range addr.Ipv4s {
-		wg.Add(1)
-		go func(target string, wg *sync.WaitGroup) {
-			defer wg.Done()
-			out := kyoketsu.PortWalk(target, kyoketsu.RetrieveScanDirectives())
-			if len(out.ListeningPorts) > 0 {
-				dns, _ := net.LookupAddr(out.IpAddress)
-				out.Fqdn = strings.Join(dns, ", ")
+	out := kyoketsu.NetSweep(addr.Ipv4s, kyoketsu.RetrieveScanDirectives())
+	for x := range out {
+		fmt.Printf("%+v\n", x)
+	}
+	/*
+		for i := range addr.Ipv4s {
+			wg.Add(1)
+			go func(target string, wg *sync.WaitGroup) {
+				defer wg.Done()
+				out := kyoketsu.PortWalk(target, kyoketsu.RetrieveScanDirectives())
+				if len(out) > 0 {
 
-				fmt.Print(" |-|-|-| :::: HOST FOUND :::: |-|-|-|\n==================||==================\n")
-				fmt.Printf("Hostname: %s\nIPv4 Address: %s\nPing Response?: %v\nListening Ports: %s\n=====================================\n", out.Fqdn, out.IpAddress, out.PingResponse, out.PortString)
+					fmt.Print(" |-|-|-| :::: HOST FOUND :::: |-|-|-|\n==================||==================\n")
+					fmt.Printf("IPv4 Address: %s\nListening Ports: %v\n=====================================\n", target, out)
 
-			}
+				}
 
-		}(addr.Ipv4s[i].String(), &wg)
+			}(addr.Ipv4s[i].String(), wg)
 
-	}
-	wg.Wait()
+		}
+		wg.Wait()
+	*/
 }

+ 13 - 17
pkg/local.go

@@ -105,17 +105,16 @@ Recursive function to get all of the IPv4 addresses for each IPv4 network that t
 		:param max: This is safety feature to prevent stack overflows, so you can manually set the depth to
 		            call the function
 */
-func addressRecurse(ipmap *IpSubnetMapper) {
+func addressRecurse(ipmap IpSubnetMapper) IpSubnetMapper {
 
-	next := getNextAddr(ipmap.Current.String())
+	next := getNextAddr(ipmap.Ipv4s[len(ipmap.Ipv4s)-1].String())
 
 	if getNetwork(next, ipmap.Mask) != ipmap.NetworkAddr.String() {
-		return
+		return ipmap
 	}
-	ipmap.Current = net.ParseIP(next)
 
 	ipmap.Ipv4s = append(ipmap.Ipv4s, net.ParseIP(next))
-	addressRecurse(ipmap)
+	return addressRecurse(ipmap)
 }
 
 /*
@@ -124,23 +123,20 @@ i.e. '192.168.50.1/24'
 
 	:param addr: the ipv4 address to use for subnet discovery
 */
-func GetNetworkAddresses(addr string) (*IpSubnetMapper, error) {
-	ipmap := &IpSubnetMapper{Ipv4s: []net.IP{}}
-
-	ip, net, err := net.ParseCIDR(addr)
+func GetNetworkAddresses(addr string) (IpSubnetMapper, error) {
+	ip, ntwrk, err := net.ParseCIDR(addr)
 	if err != nil {
-		return nil, err
+		return IpSubnetMapper{}, err
 	}
 	mask, err := strconv.Atoi(strings.Split(addr, "/")[1])
 	if err != nil {
-		return nil, err
+		return IpSubnetMapper{}, err
 	}
-	ipmap.NetworkAddr = ip.Mask(net.Mask)
-	ipmap.Mask = mask
-	ipmap.Current = ip.Mask(net.Mask)
-	addressRecurse(ipmap)
-
-	return ipmap, nil
+	ipmap := IpSubnetMapper{Ipv4s: []net.IP{ip},
+		NetworkAddr: ip.Mask(ntwrk.Mask),
+		Mask:        mask,
+		Current:     ip.Mask(ntwrk.Mask)}
+	return addressRecurse(ipmap), nil
 
 }
 

+ 2 - 4
pkg/local_test.go

@@ -86,13 +86,11 @@ func TestAddressRecurse(t *testing.T) {
 		if err != nil {
 			t.Errorf("Test case: '%s' failed! Reason: %s", tc[i].Name, err)
 		}
-		got := &IpSubnetMapper{}
-		got.Mask = tc[i].InputMask
-		got.NetworkAddr = addr.Mask(network.Mask)
-		got.Current = addr.Mask(network.Mask)
+		got := IpSubnetMapper{Mask: tc[i].InputMask, NetworkAddr: addr.Mask(network.Mask), Ipv4s: []net.IP{addr}}
 		addressRecurse(got)
 		want := LoadTestAddresses(tc[i].TestData)
 		for x := range got.Ipv4s {
+			t.Logf("%s\n", got.Ipv4s[x])
 			gotip := got.Ipv4s[x]
 			_, ok := want[gotip.String()]
 			if !ok {

+ 77 - 54
pkg/scanner.go

@@ -26,10 +26,9 @@ package kyoketsu
 
 import (
 	"fmt"
+	"log"
 	"net"
-	"strconv"
-	"strings"
-	"sync"
+	"syscall"
 	"time"
 )
 
@@ -38,10 +37,10 @@ Need to work with with a database schema in mind, and revolve functionality arou
 */
 
 type Host struct {
-	Fqdn           string         // The FQDN of the address targeted as per the systems default resolver
-	IpAddress      string         // the IPv4 address (no ipv6 support yet)
-	PingResponse   bool           // boolean value representing if the host responded to ICMP
-	ListeningPorts map[int]string // list of maps depicting a port number -> service name
+	Fqdn           string // The FQDN of the address targeted as per the systems default resolver
+	IpAddress      string // the IPv4 address (no ipv6 support yet)
+	PingResponse   bool   // boolean value representing if the host responded to ICMP
+	ListeningPorts []int  // list of maps depicting a port number -> service name
 	PortString     string
 	Id             int64
 }
@@ -50,39 +49,17 @@ type Host struct {
 Perform a concurrent TCP port dial on a host, either by domain name or IP.
 
 	:param addr: the address of fqdn to scan
-	:param portmap: a key/value pair of port numbers to service names to dial the host with
+	:param ports a list of port numbers to dial the host with
 */
-func PortWalk(addr string, portmap map[int]string) Host {
-	wg := &sync.WaitGroup{}
-	mu := &sync.Mutex{}
-	out := map[int]string{}
-	for p, s := range portmap {
-		wg.Add(1)
-		go func(target string, p int, s string, mu *sync.Mutex) {
-			defer wg.Done()
-			scanout := singlePortScan(target, p, s)
-			if scanout.Listening {
-				mu.Lock()
-				out[scanout.PortNumber] = scanout.Service
-				mu.Unlock()
-			}
-		}(addr, p, s, mu)
-	}
-	wg.Wait()
-	var dnames string
-	var portstring string
-	dns, _ := net.LookupAddr(addr)
-	dnames = strings.Join(dns, ", ")
-	for key, _ := range out {
-		portstring = portstring + "," + strconv.Itoa(key)
-	}
-	return Host{
-		IpAddress:      addr,
-		Fqdn:           dnames,
-		PingResponse:   false,
-		ListeningPorts: out,
-		PortString:     portstring,
+func PortWalk(addr string, ports []int) []int {
+	out := []int{}
+	for i := range ports {
+		p := singlePortScan(addr, ports[i])
+		if p != 0 {
+			out = append(out, p)
+		}
 	}
+	return out
 
 }
 
@@ -98,13 +75,15 @@ type PortScanResult struct {
 Wrapper function to dependency inject the resource for a port -> service name mapping.
 May move to a database, or something.
 */
-func RetrieveScanDirectives() map[int]string {
-	var portmap = map[int]string{
+func RetrieveScanDirectives() []int {
+
+	var portmap = []int{22, 443, 8080, 4379, 445, 53, 153, 27017}
+	/*map[int]string{
 		22: "ssh", 23: "telnet", 53: "dns", 80: "http", 25: "smtp", 443: "https", 8080: "unknown", 8081: "unknown",
 		8082: "unknown", 8085: "unknown", 8090: "unknown", 8091: "unknown", 9010: "unknown", 9012: "unknown", 10000: "unknown", 1433: "microsoft_sql",
 		3306: "mysql", 3050: "firebird", 5432: "postgres", 27017: "mongo", 6379: "redis", 8005: "tomcat", 6443: "kubernetes", 853: "dns-tls", 143: "imap",
 		389: "ldap", 445: "smb", 543: "kerberos", 544: "kerberos", 749: "kerberos", 760: "kerberos",
-	}
+	} */
 	return portmap
 }
 
@@ -115,13 +94,16 @@ Scans a single host on a single port
 	:param port: the port number to dial
 	:param svcs: the name of the service that the port is associate with
 */
-func singlePortScan(addr string, port int, svcs string) PortScanResult {
-	address := fmt.Sprintf("%v:%d", addr, port)
-	_, err := net.DialTimeout("tcp", address, 2*time.Second)
+func singlePortScan(addr string, port int) int {
+
+	conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%d", addr, port), 2*time.Second)
 	if err != nil {
-		return PortScanResult{PortNumber: port, Protocol: "tcp", Service: svcs, Listening: false}
+		return 0
+		//	return PortScanResult{PortNumber: port, Protocol: "tcp", Listening: false}
 	}
-	return PortScanResult{PortNumber: port, Protocol: "tcp", Service: svcs, Listening: true}
+	conn.Close()
+	return port
+	//return PortScanResult{PortNumber: port, Protocol: "tcp", Listening: true}
 }
 
 /*
@@ -130,16 +112,57 @@ Perform a port scan sweep across an entire subnet
 	:param ip: the IPv4 address WITH CIDR notation
 	:param portmap: the mapping of ports to scan with (port number mapped to protocol name)
 */
-func NetSweep(ips []net.IP, portmap map[int]string, scanned chan Host) {
-	wg := &sync.WaitGroup{}
+func NetSweep(ips []net.IP, ports []int) []Host {
+	scanned := make(chan Host)
 	for i := range ips {
-		wg.Add(1)
-		go func(target string, wg *sync.WaitGroup) {
-			defer wg.Done()
-			scanned <- PortWalk(target, portmap)
+		go func(target string, portnum []int) {
+			scanned <- Host{
+				IpAddress:      target,
+				ListeningPorts: PortWalk(target, portnum),
+			}
+
+		}(ips[i].String(), ports)
+	}
+	var hosts []Host
+	for x := range scanned {
+		fmt.Printf("%+v\n", x)
+
+		hosts = append(hosts, x)
+	}
+
+	return hosts
+
+}
 
-		}(ips[i].String(), wg)
+/*
+Create a new TCP dialer to share in a goroutine
+*/
+func NewDialer() net.Dialer {
+	return net.Dialer{}
+}
+
+/*
+Create a new low level networking interface socket
+:param intf: the name of the interface to bind the socket to
+*/
+func NewTCPSock(interfaceName string) *syscall.SockaddrLinklayer {
+	sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, syscall.ETH_P_IP)
+	if err != nil {
+		log.Fatal(err, " Could not create raw AF_PACKET socket.\n")
+	}
+	defer syscall.Close(sock)
+	intf, err := net.InterfaceByName(interfaceName)
+	if err != nil {
+		log.Fatal(err, " Couldnt locate that interface. Are you sure you mean to pass ", interfaceName, " ?")
 	}
-	wg.Wait()
+	return &syscall.SockaddrLinklayer{
+		Protocol: htons(syscall.ETH_P_IP),
+		Ifindex:  intf.Index,
+	}
+
+}
 
+// htons converts a uint16 from host- to network byte order.
+func htons(i uint16) uint16 {
+	return (i<<8)&0xff00 | i>>8
 }

+ 21 - 11
pkg/scanner_test.go

@@ -33,6 +33,18 @@ import (
 	"testing"
 )
 
+func Equal(a, b []int) bool {
+	if len(a) != len(b) {
+		return false
+	}
+	for i, v := range a {
+		if v != b[i] {
+			return false
+		}
+	}
+	return true
+}
+
 func startTestSever(port int, t *testing.T) {
 	listen, err := net.Listen("tcp", fmt.Sprintf("localhost:%v", port))
 	if err != nil {
@@ -52,36 +64,34 @@ func startTestSever(port int, t *testing.T) {
 func TestPortWalk(t *testing.T) {
 	type TestCase struct {
 		Name       string
-		ScanPort   map[int]string
-		ListenPort map[int]string
+		ScanPort   []int
+		ListenPort []int
 		ShouldFail bool
 	}
 	tc := []TestCase{
 		TestCase{
 			Name:       "Passing test, listening on 8080/27017 and scanned on 8080/27017.",
-			ScanPort:   map[int]string{8080: "unknown", 27017: "mongo"},
-			ListenPort: map[int]string{8080: "unknown", 27017: "mongo"},
+			ScanPort:   []int{8080, 27017},
+			ListenPort: []int{8080, 27017},
 			ShouldFail: false,
 		},
 		TestCase{
 			Name:       "Failing test, listening on 8081 scanned on 6439",
-			ScanPort:   map[int]string{6439: "random_service"},
-			ListenPort: map[int]string{8081: "unknown"},
+			ScanPort:   []int{6439},
+			ListenPort: []int{8081},
 			ShouldFail: true,
 		},
 	}
 	wg := &sync.WaitGroup{}
 	for i := range tc {
-		for port, _ := range tc[i].ListenPort {
+		for x := range tc[i].ListenPort {
 			wg.Add(1)
-			go startTestSever(port, t)
+			go startTestSever(tc[i].ListenPort[x], t)
 		}
 		got := PortWalk("localhost", tc[i].ScanPort)
 		for k, _ := range tc[i].ListenPort {
 			wg.Done()
-
-			_, ok := got.ListeningPorts[k]
-			if !ok {
+			if !Equal(got, tc[i].ListenPort) {
 				if !tc[i].ShouldFail {
 					t.Errorf("Test '%s' failed! PortWalk didnt detect the test server was listening on: %+v\n", tc[i].Name, tc[i].ListenPort)
 				}

+ 26 - 14
pkg/webserver.go

@@ -26,7 +26,7 @@ Run a new webserver
 
 	:param port: port number to run the webserver on
 */
-func RunHttpServer(port int, dbhook TopologyDatabaseIO, portmap map[int]string) {
+func RunHttpServer(port int, dbhook TopologyDatabaseIO, portmap []int) {
 	assets := &AssetHandler{Root: content, RelPath: "static", EmbedRoot: "html"}
 	tmpl, err := template.ParseFS(content, "html/templates/*.html")
 	if err != nil {
@@ -49,7 +49,7 @@ func RunHttpServer(port int, dbhook TopologyDatabaseIO, portmap map[int]string)
 type ExecutionHandler struct {
 	DbHook     TopologyDatabaseIO
 	TableEntry *template.Template
-	PortMap    map[int]string
+	PortMap    []int
 }
 
 func (e *ExecutionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -70,20 +70,32 @@ func (e *ExecutionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		fmt.Fprintf(w, "There was an error processing your request: %s", err)
 	}
-	scanned := make(chan Host, len(subnetMap.Ipv4s))
-	NetSweep(subnetMap.Ipv4s, RetrieveScanDirectives(), scanned)
-	close(scanned)
-	for host := range scanned {
-		if len(host.ListeningPorts) > 0 {
-			e.TableEntry.Execute(w, host)
-			continue
-		} else if host.Fqdn != "" {
-			e.TableEntry.Execute(w, host)
-			continue
-		} else if host.PingResponse {
-			e.TableEntry.Execute(w, host)
+	scanned := NetSweep(subnetMap.Ipv4s, RetrieveScanDirectives())
+	for i := range scanned {
+		if len(scanned[i].ListeningPorts) > 0 {
+			e.TableEntry.Execute(w, scanned[i])
+			/*
+				rec, err := e.DbHook.GetByIP(host.IpAddress)
+				if err != nil {
+					if err != ErrNotExists {
+						log.Fatal(err, "There was a fatal error querying the database\n")
+					}
+					_, err = e.DbHook.Create(host)
+					if err != nil {
+						log.Fatal(err, "There was a fatal error creating this record in the database.\n")
+					}
+					continue
+				}
+				_, err = e.DbHook.Update(rec.Id, host)
+				if err != nil {
+					log.Fatal(err, "there was a fatal error updating this record: ", rec)
+				}
+				continue
+			*/
 			continue
+			// not adding these to the database because this isnt really that important
 		}
+
 	}
 
 }