|
@@ -2,10 +2,15 @@ package kyoketsu
|
|
|
|
|
|
import (
|
|
|
"fmt"
|
|
|
+ "log"
|
|
|
"net"
|
|
|
- "net/netip"
|
|
|
+ "os"
|
|
|
"sync"
|
|
|
"time"
|
|
|
+
|
|
|
+ "github.com/go-ping/ping"
|
|
|
+ "golang.org/x/net/icmp"
|
|
|
+ "golang.org/x/net/ipv4"
|
|
|
)
|
|
|
|
|
|
var PORT_MAP = map[int]string{
|
|
@@ -13,23 +18,77 @@ var PORT_MAP = map[int]string{
|
|
|
8082: "unknown", 8085: "unknown", 8090: "unknown", 8091: "unknown", 9010: "unknown", 9012: "unknown", 10000: "unknown",
|
|
|
}
|
|
|
|
|
|
-type TcpScanHost struct {
|
|
|
- Host string `json:"host"`
|
|
|
- Ipv4Address netip.Addr `json:"ipv4_address"`
|
|
|
- PortsScanned []PortScanResult `json:"ports_scanned"`
|
|
|
+/*
|
|
|
+Need to work with with a database schema in mind, and revolve functionality around that
|
|
|
+*/
|
|
|
+
|
|
|
+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
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+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
|
|
|
+*/
|
|
|
+func PortWalk(addr string, portmap map[int]string) *Host {
|
|
|
+ wg := &sync.WaitGroup{}
|
|
|
+ out := make(chan *PortScanResult)
|
|
|
+
|
|
|
+ wgOuter := &sync.WaitGroup{}
|
|
|
+ wgOuter.Add(1)
|
|
|
+ go func() {
|
|
|
+ defer wgOuter.Done()
|
|
|
+ ports := RetrieveScanDirectives()
|
|
|
+ for p, s := range ports.Pairs {
|
|
|
+ wg.Add(1)
|
|
|
+ port := p
|
|
|
+ svcs := s
|
|
|
+ go func() {
|
|
|
+ out <- singlePortScan(addr, port, svcs)
|
|
|
+ wg.Done()
|
|
|
+ }()
|
|
|
+ }
|
|
|
+ wg.Wait()
|
|
|
+ close(out)
|
|
|
+
|
|
|
+ }()
|
|
|
+
|
|
|
+ host := &Host{IpAddress: addr, ListeningPorts: []map[int]string{}}
|
|
|
+
|
|
|
+ for result := range out {
|
|
|
+ if result.Listening {
|
|
|
+ host.ListeningPorts = append(host.ListeningPorts, map[int]string{
|
|
|
+ result.PortNumber: result.Service,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ wgOuter.Wait()
|
|
|
+ return host
|
|
|
+
|
|
|
}
|
|
|
|
|
|
type PortScanResult struct {
|
|
|
- PortNumber int `json:"port_number"`
|
|
|
- Service string `json:"service"`
|
|
|
- Protocol string `json:"protocol"`
|
|
|
- Listening bool `json:"listening"`
|
|
|
+ // This is used to represent the results of a port scan against one host
|
|
|
+ PortNumber int `json:"port_number"` // The port number that was scanned
|
|
|
+ Service string `json:"service"` // the name of the service that the port was identified/mapped to
|
|
|
+ Protocol string `json:"protocol"` // The IP protocol (TCP/UDP)
|
|
|
+ Listening bool `json:"listening"` // A boolean value that depicts if the service is listening or not
|
|
|
}
|
|
|
|
|
|
type PortScanDirective struct {
|
|
|
+ // Struct for dependency injecting the dynamic port map used for scans
|
|
|
Pairs map[int]string
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+Wrapper function to dependency inject the resource for a port -> service name mapping.
|
|
|
+May move to a database, or something.
|
|
|
+*/
|
|
|
func RetrieveScanDirectives() PortScanDirective {
|
|
|
return PortScanDirective{Pairs: PORT_MAP}
|
|
|
}
|
|
@@ -39,9 +98,9 @@ Scans a single host on a single port
|
|
|
|
|
|
:param addr: the address to dial
|
|
|
: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 {
|
|
|
- //defer wg.Done()
|
|
|
address := fmt.Sprintf("%v:%d", addr, port)
|
|
|
conn, err := net.DialTimeout("tcp", address, 1*time.Second)
|
|
|
if err != nil {
|
|
@@ -52,46 +111,92 @@ func singlePortScan(addr string, port int, svcs string) *PortScanResult {
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-Perform a TCP scan on the pointers host
|
|
|
+This function makes use of an external dependency, may or may not keep. It still needs to be implemented
|
|
|
|
|
|
- :param posts: a list of ports to scan against a host
|
|
|
+ :param addr: the ip address to send an ICMP request to
|
|
|
+ :param pinger: a pointer to a ping.Pinger struct to reuse
|
|
|
*/
|
|
|
-func (t *TcpScanHost) ScanPortRange(ports []int) []*PortScanResult {
|
|
|
- wg := &sync.WaitGroup{}
|
|
|
- out := make(chan *PortScanResult)
|
|
|
- var res []*PortScanResult
|
|
|
- wgOuter := &sync.WaitGroup{}
|
|
|
- wgOuter.Add(1)
|
|
|
- go func() {
|
|
|
- defer wgOuter.Done()
|
|
|
- ports := RetrieveScanDirectives()
|
|
|
- for p, s := range ports.Pairs {
|
|
|
- wg.Add(1)
|
|
|
- port := p
|
|
|
- svcs := s
|
|
|
- go func() {
|
|
|
- out <- singlePortScan(t.Ipv4Address.String(), port, svcs)
|
|
|
- wg.Done()
|
|
|
- }()
|
|
|
- }
|
|
|
- wg.Wait()
|
|
|
- close(out)
|
|
|
+func PingTarget(addr string, pinger *ping.Pinger) bool {
|
|
|
+ err := pinger.SetAddr(addr)
|
|
|
+ if err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ err = pinger.Run()
|
|
|
+ if err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ stats := pinger.Statistics()
|
|
|
+ if stats.PacketsRecv > 0 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
|
|
|
- }()
|
|
|
- for result := range out {
|
|
|
- if !result.Listening {
|
|
|
- continue
|
|
|
- }
|
|
|
- res = append(res, result)
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+Listen for ICMP responses on a specific address
|
|
|
+
|
|
|
+ :param listening: the address of your server
|
|
|
+*/
|
|
|
+func IcmpListen(listening string) *icmp.PacketConn {
|
|
|
+ icmpSrv, err := icmp.ListenPacket("udp4", listening)
|
|
|
+ icmpSrv.SetDeadline(time.Now().Add(5 * time.Second))
|
|
|
+ if err != nil {
|
|
|
+ log.Println(err)
|
|
|
}
|
|
|
- wgOuter.Wait()
|
|
|
- return res
|
|
|
+ return icmpSrv
|
|
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-Evaluate whether the host has an entry in the ARP table
|
|
|
+Send an ICMP request to an address and listen for a response (UNFINISHED/NON-FUNCTIONAL)
|
|
|
+
|
|
|
+ :param icmpSrv: a pointer to an ICMP PacketConn struct, for reading and sending ICMP requests.
|
|
|
+ :param addr: the address to send the request to
|
|
|
*/
|
|
|
-func (h *TcpScanHost) IsOnline() bool {
|
|
|
+func icmpAsk(icmpSrv *icmp.PacketConn, addr string) bool {
|
|
|
+ icmpReq := icmp.Message{
|
|
|
+ Type: ipv4.ICMPTypeEcho, Code: 0,
|
|
|
+ Body: &icmp.Echo{
|
|
|
+ ID: os.Getpid() & 0xffff, Seq: 1,
|
|
|
+ Data: []byte("DIALING FROM KYOKETSU"),
|
|
|
+ },
|
|
|
+ }
|
|
|
+ icmpB, err := icmpReq.Marshal(nil)
|
|
|
+ if err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if _, err := icmpSrv.WriteTo(icmpB, &net.UDPAddr{IP: net.ParseIP(addr)}); err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ respB := make([]byte, 1500)
|
|
|
+ n, peer, err := icmpSrv.ReadFrom(respB)
|
|
|
+ if err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ rm, err := icmp.ParseMessage(ipv4.ICMPTypeEcho.Protocol(), respB[:n])
|
|
|
+ if err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ switch rm.Type {
|
|
|
+ case ipv4.ICMPTypeEchoReply:
|
|
|
+ echo, ok := rm.Body.(*icmp.Echo)
|
|
|
+ if !ok {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if peer.(*net.UDPAddr).IP.String() == addr && echo.Seq == 1 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ return false
|
|
|
+
|
|
|
+ }
|
|
|
return false
|
|
|
+
|
|
|
}
|