scanner.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. "log"
  24. "net"
  25. "os"
  26. "sync"
  27. "time"
  28. "github.com/go-ping/ping"
  29. "golang.org/x/net/icmp"
  30. "golang.org/x/net/ipv4"
  31. )
  32. var PORT_MAP = map[int]string{
  33. 22: "ssh", 23: "telnet", 53: "dns", 80: "http", 25: "smtp", 443: "https", 8080: "unknown", 8081: "unknown",
  34. //8082: "unknown", 8085: "unknown", 8090: "unknown", 8091: "unknown", 9010: "unknown", 9012: "unknown", 10000: "unknown", 1433: "microsoft_sql",
  35. 3306: "mysql", 3050: "firebird", 5432: "postgres", 27017: "mongo", 6379: "redis", 8005: "tomcat", 6443: "kubernetes", 853: "dns-tls", 143: "imap",
  36. 389: "ldap", 445: "smb", 543: "kerberos", 544: "kerberos", 749: "kerberos", 760: "kerberos",
  37. }
  38. /*
  39. Need to work with with a database schema in mind, and revolve functionality around that
  40. */
  41. type Host struct {
  42. Fqdn string // The FQDN of the address targeted as per the systems default resolver
  43. IpAddress string // the IPv4 address (no ipv6 support yet)
  44. PingResponse bool // boolean value representing if the host responded to ICMP
  45. ListeningPorts map[int]string // list of maps depicting a port number -> service name
  46. }
  47. /*
  48. Perform a concurrent TCP port dial on a host, either by domain name or IP.
  49. :param addr: the address of fqdn to scan
  50. :param portmap: a key/value pair of port numbers to service names to dial the host with
  51. */
  52. func PortWalk(addr string, portmap map[int]string) *Host {
  53. wg := &sync.WaitGroup{}
  54. out := []*PortScanResult{}
  55. for p, s := range portmap {
  56. wg.Add(1)
  57. go func(target string, p int, s string) {
  58. defer wg.Done()
  59. out = append(out, singlePortScan(target, p, s))
  60. }(addr, p, s)
  61. }
  62. wg.Wait()
  63. host := &Host{IpAddress: addr, ListeningPorts: map[int]string{}}
  64. for i := range out {
  65. if out[i].Listening {
  66. host.ListeningPorts[out[i].PortNumber] = out[i].Service
  67. }
  68. }
  69. return host
  70. }
  71. type PortScanResult struct {
  72. // This is used to represent the results of a port scan against one host
  73. PortNumber int `json:"port_number"` // The port number that was scanned
  74. Service string `json:"service"` // the name of the service that the port was identified/mapped to
  75. Protocol string `json:"protocol"` // The IP protocol (TCP/UDP)
  76. Listening bool `json:"listening"` // A boolean value that depicts if the service is listening or not
  77. }
  78. type PortScanDirective struct {
  79. // Struct for dependency injecting the dynamic port map used for scans
  80. Pairs map[int]string
  81. }
  82. /*
  83. Wrapper function to dependency inject the resource for a port -> service name mapping.
  84. May move to a database, or something.
  85. */
  86. func RetrieveScanDirectives() PortScanDirective {
  87. return PortScanDirective{Pairs: PORT_MAP}
  88. }
  89. /*
  90. Scans a single host on a single port
  91. :param addr: the address to dial
  92. :param port: the port number to dial
  93. :param svcs: the name of the service that the port is associate with
  94. */
  95. func singlePortScan(addr string, port int, svcs string) *PortScanResult {
  96. address := fmt.Sprintf("%v:%d", addr, port)
  97. conn, err := net.DialTimeout("tcp", address, 5*time.Second)
  98. if err != nil {
  99. return &PortScanResult{PortNumber: port, Protocol: "tcp", Service: svcs, Listening: false}
  100. }
  101. conn.Close()
  102. return &PortScanResult{PortNumber: port, Protocol: "tcp", Service: svcs, Listening: true}
  103. }
  104. /*
  105. This function makes use of an external dependency, may or may not keep. It still needs to be implemented
  106. :param addr: the ip address to send an ICMP request to
  107. :param pinger: a pointer to a ping.Pinger struct to reuse
  108. */
  109. func PingTarget(addr string, pinger *ping.Pinger) bool {
  110. err := pinger.SetAddr(addr)
  111. if err != nil {
  112. log.Println(err)
  113. return false
  114. }
  115. err = pinger.Run()
  116. if err != nil {
  117. log.Println(err)
  118. return false
  119. }
  120. stats := pinger.Statistics()
  121. if stats.PacketsRecv > 0 {
  122. return true
  123. }
  124. return false
  125. }
  126. /*
  127. Listen for ICMP responses on a specific address
  128. :param listening: the address of your server
  129. */
  130. func IcmpListen(listening string) *icmp.PacketConn {
  131. icmpSrv, err := icmp.ListenPacket("udp4", listening)
  132. icmpSrv.SetDeadline(time.Now().Add(5 * time.Second))
  133. if err != nil {
  134. log.Println(err)
  135. }
  136. return icmpSrv
  137. }
  138. /*
  139. Send an ICMP request to an address and listen for a response (UNFINISHED/NON-FUNCTIONAL)
  140. :param icmpSrv: a pointer to an ICMP PacketConn struct, for reading and sending ICMP requests.
  141. :param addr: the address to send the request to
  142. */
  143. func icmpAsk(icmpSrv *icmp.PacketConn, addr string) bool {
  144. icmpReq := icmp.Message{
  145. Type: ipv4.ICMPTypeEcho, Code: 0,
  146. Body: &icmp.Echo{
  147. ID: os.Getpid() & 0xffff, Seq: 1,
  148. Data: []byte("DIALING FROM KYOKETSU"),
  149. },
  150. }
  151. icmpB, err := icmpReq.Marshal(nil)
  152. if err != nil {
  153. log.Println(err)
  154. return false
  155. }
  156. if _, err := icmpSrv.WriteTo(icmpB, &net.UDPAddr{IP: net.ParseIP(addr)}); err != nil {
  157. log.Println(err)
  158. return false
  159. }
  160. respB := make([]byte, 1500)
  161. n, peer, err := icmpSrv.ReadFrom(respB)
  162. if err != nil {
  163. log.Println(err)
  164. return false
  165. }
  166. rm, err := icmp.ParseMessage(ipv4.ICMPTypeEcho.Protocol(), respB[:n])
  167. if err != nil {
  168. log.Println(err)
  169. return false
  170. }
  171. switch rm.Type {
  172. case ipv4.ICMPTypeEchoReply:
  173. echo, ok := rm.Body.(*icmp.Echo)
  174. if !ok {
  175. return false
  176. }
  177. if peer.(*net.UDPAddr).IP.String() == addr && echo.Seq == 1 {
  178. return true
  179. }
  180. default:
  181. return false
  182. }
  183. return false
  184. }