scanner.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /*
  2. GNU GENERAL PUBLIC LICENSE
  3. Version 3, 29 June 2007
  4. kyoketsu, a Client-To-Client Network Enumeration System
  5. Copyright (C) 2024 Russell Hrubesky, ChiralWorks Software LLC
  6. Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
  7. Everyone is permitted to copy and distribute verbatim copies
  8. of this license document, but changing it is not allowed.
  9. This program is free software: you can redistribute it and/or modify
  10. it under the terms of the GNU General Public License as published by
  11. the Free Software Foundation, either version 3 of the License,
  12. or (at your option) any later version.
  13. This program is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16. See the GNU General Public License for more details.
  17. You should have received a copy of the GNU General Public License
  18. along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. package kyoketsu
  21. import (
  22. "fmt"
  23. "net"
  24. "strings"
  25. "sync"
  26. "time"
  27. )
  28. var PORT_MAP = map[int]string{
  29. 22: "ssh", 23: "telnet", 53: "dns", 80: "http", 25: "smtp", 443: "https", 8080: "unknown", 8081: "unknown",
  30. //8082: "unknown", 8085: "unknown", 8090: "unknown", 8091: "unknown", 9010: "unknown", 9012: "unknown", 10000: "unknown", 1433: "microsoft_sql",
  31. 3306: "mysql", 3050: "firebird", 5432: "postgres", 27017: "mongo", 6379: "redis", 8005: "tomcat", 6443: "kubernetes", 853: "dns-tls", 143: "imap",
  32. 389: "ldap", 445: "smb", 543: "kerberos", 544: "kerberos", 749: "kerberos", 760: "kerberos",
  33. }
  34. /*
  35. Need to work with with a database schema in mind, and revolve functionality around that
  36. */
  37. type Host struct {
  38. Fqdn string // The FQDN of the address targeted as per the systems default resolver
  39. IpAddress string // the IPv4 address (no ipv6 support yet)
  40. PingResponse bool // boolean value representing if the host responded to ICMP
  41. ListeningPorts map[int]string // list of maps depicting a port number -> service name
  42. PortString string
  43. Id int64
  44. }
  45. /*
  46. Perform a concurrent TCP port dial on a host, either by domain name or IP.
  47. :param addr: the address of fqdn to scan
  48. :param portmap: a key/value pair of port numbers to service names to dial the host with
  49. */
  50. func PortWalk(addr string, portmap map[int]string) *Host {
  51. wg := &sync.WaitGroup{}
  52. mu := &sync.Mutex{}
  53. out := []*PortScanResult{}
  54. for p, s := range portmap {
  55. wg.Add(1)
  56. go func(target string, p int, s string, mu *sync.Mutex) {
  57. defer wg.Done()
  58. scanout := singlePortScan(target, p, s)
  59. mu.Lock()
  60. out = append(out, scanout)
  61. mu.Unlock()
  62. }(addr, p, s, mu)
  63. }
  64. wg.Wait()
  65. host := &Host{IpAddress: addr, ListeningPorts: map[int]string{}}
  66. for i := range out {
  67. if out[i].Listening {
  68. host.ListeningPorts[out[i].PortNumber] = out[i].Service
  69. host.PortString = fmt.Sprintf("%s,%v", host.PortString, out[i].PortNumber)
  70. }
  71. host.PortString = strings.TrimPrefix(host.PortString, ",")
  72. }
  73. return host
  74. }
  75. type PortScanResult struct {
  76. // This is used to represent the results of a port scan against one host
  77. PortNumber int `json:"port_number"` // The port number that was scanned
  78. Service string `json:"service"` // the name of the service that the port was identified/mapped to
  79. Protocol string `json:"protocol"` // The IP protocol (TCP/UDP)
  80. Listening bool `json:"listening"` // A boolean value that depicts if the service is listening or not
  81. }
  82. type PortScanDirective struct {
  83. // Struct for dependency injecting the dynamic port map used for scans
  84. Pairs map[int]string
  85. }
  86. /*
  87. Wrapper function to dependency inject the resource for a port -> service name mapping.
  88. May move to a database, or something.
  89. */
  90. func RetrieveScanDirectives() PortScanDirective {
  91. return PortScanDirective{Pairs: PORT_MAP}
  92. }
  93. /*
  94. Scans a single host on a single port
  95. :param addr: the address to dial
  96. :param port: the port number to dial
  97. :param svcs: the name of the service that the port is associate with
  98. */
  99. func singlePortScan(addr string, port int, svcs string) *PortScanResult {
  100. address := fmt.Sprintf("%v:%d", addr, port)
  101. conn, err := net.DialTimeout("tcp", address, 5*time.Second)
  102. if err != nil {
  103. return &PortScanResult{PortNumber: port, Protocol: "tcp", Service: svcs, Listening: false}
  104. }
  105. conn.Close()
  106. return &PortScanResult{PortNumber: port, Protocol: "tcp", Service: svcs, Listening: true}
  107. }
  108. /*
  109. Perform a port scan sweep across an entire subnet
  110. :param ip: the IPv4 address WITH CIDR notation
  111. :param portmap: the mapping of ports to scan with (port number mapped to protocol name)
  112. */
  113. func NetSweep(ip string, portmap map[int]string) ([]*Host, error) {
  114. var err error
  115. var addr *IpSubnetMapper
  116. addr, err = GetNetworkAddresses(ip)
  117. if err != nil {
  118. return nil, err
  119. }
  120. returnhosts := []*Host{}
  121. var wg sync.WaitGroup
  122. mu := &sync.Mutex{}
  123. for i := range addr.Ipv4s {
  124. wg.Add(1)
  125. go func(target string, mu *sync.Mutex) {
  126. defer wg.Done()
  127. out := PortWalk(target, portmap)
  128. if len(out.ListeningPorts) > 0 {
  129. dns, _ := net.LookupAddr(out.IpAddress)
  130. out.Fqdn = strings.Join(dns, ", ")
  131. mu.Lock()
  132. returnhosts = append(returnhosts, out)
  133. mu.Unlock()
  134. }
  135. }(addr.Ipv4s[i].String(), mu)
  136. }
  137. wg.Wait()
  138. return returnhosts, nil
  139. }