scanner.go 5.4 KB

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