scanner.go 5.1 KB

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