scanner.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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. "context"
  23. "fmt"
  24. "log"
  25. "net"
  26. "strings"
  27. "sync"
  28. "syscall"
  29. "time"
  30. )
  31. /*
  32. Need to work with with a database schema in mind, and revolve functionality around that
  33. */
  34. type Host struct {
  35. Fqdn string // The FQDN of the address targeted as per the systems default resolver
  36. IpAddress string // the IPv4 address (no ipv6 support yet)
  37. PingResponse bool // boolean value representing if the host responded to ICMP
  38. ListeningPorts []int // list of maps depicting a port number -> service name
  39. Network string
  40. PortString string
  41. Id int64
  42. }
  43. /*
  44. Perform a concurrent TCP port dial on a host, either by domain name or IP.
  45. :param addr: the address of fqdn to scan
  46. :param ports a list of port numbers to dial the host with
  47. */
  48. func PortWalk(addr string, ports []int) []int {
  49. out := []int{}
  50. for i := range ports {
  51. p := singlePortScan(addr, ports[i])
  52. if p != 0 {
  53. out = append(out, p)
  54. }
  55. }
  56. return out
  57. }
  58. type PortScanResult struct {
  59. // This is used to represent the results of a port scan against one host
  60. PortNumber int `json:"port_number"` // The port number that was scanned
  61. Service string `json:"service"` // the name of the service that the port was identified/mapped to
  62. Protocol string `json:"protocol"` // The IP protocol (TCP/UDP)
  63. Listening bool `json:"listening"` // A boolean value that depicts if the service is listening or not
  64. }
  65. /*
  66. Wrapper function to dependency inject the resource for a port -> service name mapping.
  67. May move to a database, or something.
  68. */
  69. func RetrieveScanDirectives() []int {
  70. var portmap = []int{22, 80, 443, 8080, 4379, 445, 53, 153, 27017}
  71. return portmap
  72. }
  73. /*
  74. Scans a single host on a single port
  75. :param addr: the address to dial
  76. :param port: the port number to dial
  77. :param svcs: the name of the service that the port is associate with
  78. */
  79. func singlePortScan(addr string, port int) int {
  80. conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%d", addr, port), 2*time.Second)
  81. if err != nil {
  82. return 0
  83. // return PortScanResult{PortNumber: port, Protocol: "tcp", Listening: false}
  84. }
  85. conn.Close()
  86. return port
  87. //return PortScanResult{PortNumber: port, Protocol: "tcp", Listening: true}
  88. }
  89. /*
  90. Perform a port scan sweep across an entire subnet
  91. :param ip: the IPv4 address WITH CIDR notation
  92. :param portmap: the mapping of ports to scan with (port number mapped to protocol name)
  93. */
  94. func NetSweep(ips []net.IP, cidr int, ports []int, scanned chan Host) {
  95. wg := &sync.WaitGroup{}
  96. killswitch, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
  97. defer cancel()
  98. network := getNetwork(ips[0].String(), cidr)
  99. for i := range ips {
  100. wg.Add(1)
  101. go func(target string, ntwrk string, portnum []int, wgrp *sync.WaitGroup, output chan Host) {
  102. select {
  103. case <-killswitch.Done():
  104. fmt.Println("UNTO DEATH :::: WHILE THE SUN BEAMS DOWN")
  105. return
  106. default:
  107. defer wgrp.Done()
  108. portscanned := PortWalk(target, portnum)
  109. output <- Host{
  110. Fqdn: getFqdn(target),
  111. IpAddress: target,
  112. ListeningPorts: portscanned,
  113. PortString: strings.Trim(strings.Join(strings.Fields(fmt.Sprint(portscanned)), ","), "[]"),
  114. Network: ntwrk,
  115. }
  116. }
  117. }(ips[i].String(), network, ports, wg, scanned)
  118. }
  119. wg.Wait()
  120. close(scanned)
  121. }
  122. func getFqdn(ip string) string {
  123. names, err := net.LookupAddr(ip)
  124. if err != nil {
  125. return "not found with default resolver"
  126. }
  127. return strings.Join(names, ", ")
  128. }
  129. /*
  130. Create a new TCP dialer to share in a goroutine
  131. */
  132. func NewDialer() net.Dialer {
  133. return net.Dialer{}
  134. }
  135. /*
  136. Create a new low level networking interface socket
  137. :param intf: the name of the interface to bind the socket to
  138. */
  139. func NewTCPSock(interfaceName string) *syscall.SockaddrLinklayer {
  140. sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, syscall.ETH_P_IP)
  141. if err != nil {
  142. log.Fatal(err, " Could not create raw AF_PACKET socket.\n")
  143. }
  144. defer syscall.Close(sock)
  145. intf, err := net.InterfaceByName(interfaceName)
  146. if err != nil {
  147. log.Fatal(err, " Couldnt locate that interface. Are you sure you mean to pass ", interfaceName, " ?")
  148. }
  149. return &syscall.SockaddrLinklayer{
  150. Protocol: htons(syscall.ETH_P_IP),
  151. Ifindex: intf.Index,
  152. }
  153. }
  154. // htons converts a uint16 from host- to network byte order.
  155. func htons(i uint16) uint16 {
  156. return (i<<8)&0xff00 | i>>8
  157. }