scanner.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. "strings"
  26. "sync"
  27. "syscall"
  28. "time"
  29. )
  30. const NoFqdn = "Not found with default resolver"
  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. func (h Host) FormatUrl() string {
  44. ports := strings.Split(h.PortString, ",")
  45. var https bool
  46. var http bool
  47. var url string
  48. for i := range ports {
  49. if ports[i] == "443" {
  50. https = true
  51. }
  52. if ports[i] == "80" {
  53. http = true
  54. }
  55. }
  56. if h.Fqdn == NoFqdn {
  57. url = h.IpAddress
  58. } else {
  59. url = h.Fqdn
  60. }
  61. if https {
  62. return "https://" + strings.Trim(url, ".")
  63. }
  64. if http {
  65. return "http://" + strings.Trim(url, ".")
  66. }
  67. return "none"
  68. }
  69. /*
  70. Perform a concurrent TCP port dial on a host, either by domain name or IP.
  71. :param addr: the address of fqdn to scan
  72. :param ports a list of port numbers to dial the host with
  73. */
  74. func PortWalk(addr string, ports []int) []int {
  75. out := []int{}
  76. for i := range ports {
  77. p := singlePortScan(addr, ports[i])
  78. if p != 0 {
  79. out = append(out, p)
  80. }
  81. }
  82. return out
  83. }
  84. type PortScanResult struct {
  85. // This is used to represent the results of a port scan against one host
  86. PortNumber int `json:"port_number"` // The port number that was scanned
  87. Service string `json:"service"` // the name of the service that the port was identified/mapped to
  88. Protocol string `json:"protocol"` // The IP protocol (TCP/UDP)
  89. Listening bool `json:"listening"` // A boolean value that depicts if the service is listening or not
  90. }
  91. /*
  92. Wrapper function to dependency inject the resource for a port -> service name mapping.
  93. May move to a database, or something.
  94. */
  95. func RetrieveScanDirectives() []int {
  96. var portmap = []int{80, 443}
  97. return portmap
  98. }
  99. /*
  100. Scans a single host on a single port
  101. :param addr: the address to dial
  102. :param port: the port number to dial
  103. :param svcs: the name of the service that the port is associate with
  104. */
  105. func singlePortScan(addr string, port int) int {
  106. conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%d", addr, port), 2*time.Second)
  107. if err != nil {
  108. return 0
  109. }
  110. defer func() {
  111. if conn != nil {
  112. conn.Close()
  113. }
  114. }()
  115. return port
  116. }
  117. /*
  118. Perform a port scan sweep across an entire subnet
  119. :param ip: the IPv4 address WITH CIDR notation
  120. :param portmap: the mapping of ports to scan with (port number mapped to protocol name)
  121. */
  122. func NetSweep(ips []net.IP, cidr int, ports []int, scanned chan Host) {
  123. wg := &sync.WaitGroup{}
  124. network := getNetwork(ips[0].String(), cidr)
  125. for i := range ips {
  126. wg.Add(1)
  127. go func(target string, ntwrk string, portnum []int, output chan Host) {
  128. defer wg.Done()
  129. portscanned := PortWalk(target, portnum)
  130. output <- Host{
  131. Fqdn: getFqdn(target),
  132. IpAddress: target,
  133. ListeningPorts: portscanned,
  134. PortString: strings.Trim(strings.Join(strings.Fields(fmt.Sprint(portscanned)), ","), "[]"),
  135. Network: ntwrk,
  136. }
  137. }(ips[i].String(), network, ports, scanned)
  138. }
  139. wg.Wait()
  140. close(scanned)
  141. }
  142. func getFqdn(ip string) string {
  143. names, err := net.LookupAddr(ip)
  144. if err != nil {
  145. return NoFqdn
  146. }
  147. return strings.Join(names, ", ")
  148. }
  149. func NewSocket() int {
  150. fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_IP)))
  151. if err != nil {
  152. log.Fatal("Could not create a new socket ", err)
  153. }
  154. return fd
  155. }
  156. func BuildAndSend(intf string, fd int, srcMac net.HardwareAddr, dstMac net.HardwareAddr, srcIp net.IP, dstIp net.IP, srcPort int, dstPort int) {
  157. ifi, err := net.InterfaceByName(intf)
  158. if err != nil {
  159. log.Fatal("Interface was not found on the host: ", err)
  160. }
  161. config, err := NewPacketConfig(
  162. WithEthernetLayer(srcMac, dstMac),
  163. WithIpLayer(srcIp, dstIp),
  164. WithPayloadSize(1024),
  165. )
  166. if err != nil {
  167. log.Fatal("error configuring packet: ", err)
  168. }
  169. addr := &syscall.SockaddrLinklayer{
  170. Protocol: htons(syscall.ETH_P_IP),
  171. Ifindex: ifi.Index,
  172. }
  173. // build the packet
  174. packet, err := BuildPacket(config)
  175. if err != nil {
  176. log.Fatal("failed to build packet: %w", err)
  177. }
  178. if err = syscall.Sendto(fd, packet, 0, addr); err != nil {
  179. log.Fatal("Could not send packet out to target, ", err)
  180. }
  181. }
  182. // htons converts a uint16 from host- to network byte order.
  183. func htons(i uint16) uint16 {
  184. return (i<<8)&0xff00 | i>>8
  185. }