123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- /*
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
- kyoketsu, a Client-To-Client Network Enumeration System
- Copyright (C) 2024 Russell Hrubesky, ChiralWorks Software LLC
- Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- package kyoketsu
- import (
- "fmt"
- "log"
- "net"
- "strings"
- "sync"
- "syscall"
- "time"
- )
- const NoFqdn = "Not found with default resolver"
- /*
- Need to work with with a database schema in mind, and revolve functionality around that
- */
- type Host struct {
- Fqdn string // The FQDN of the address targeted as per the systems default resolver
- IpAddress string // the IPv4 address (no ipv6 support yet)
- PingResponse bool // boolean value representing if the host responded to ICMP
- ListeningPorts []int // list of maps depicting a port number -> service name
- Network string
- PortString string
- Id int64
- }
- func (h Host) FormatUrl() string {
- ports := strings.Split(h.PortString, ",")
- var https bool
- var http bool
- var url string
- for i := range ports {
- if ports[i] == "443" {
- https = true
- }
- if ports[i] == "80" {
- http = true
- }
- }
- if h.Fqdn == NoFqdn {
- url = h.IpAddress
- } else {
- url = h.Fqdn
- }
- if https {
- return "https://" + strings.Trim(url, ".")
- }
- if http {
- return "http://" + strings.Trim(url, ".")
- }
- return "none"
- }
- /*
- Perform a concurrent TCP port dial on a host, either by domain name or IP.
- :param addr: the address of fqdn to scan
- :param ports a list of port numbers to dial the host with
- */
- func PortWalk(addr string, ports []int) []int {
- out := []int{}
- for i := range ports {
- p := singlePortScan(addr, ports[i])
- if p != 0 {
- out = append(out, p)
- }
- }
- return out
- }
- type PortScanResult struct {
- // This is used to represent the results of a port scan against one host
- PortNumber int `json:"port_number"` // The port number that was scanned
- Service string `json:"service"` // the name of the service that the port was identified/mapped to
- Protocol string `json:"protocol"` // The IP protocol (TCP/UDP)
- Listening bool `json:"listening"` // A boolean value that depicts if the service is listening or not
- }
- /*
- Wrapper function to dependency inject the resource for a port -> service name mapping.
- May move to a database, or something.
- */
- func RetrieveScanDirectives() []int {
- var portmap = []int{80, 443}
- return portmap
- }
- /*
- Scans a single host on a single port
- :param addr: the address to dial
- :param port: the port number to dial
- :param svcs: the name of the service that the port is associate with
- */
- func singlePortScan(addr string, port int) int {
- conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%d", addr, port), 2*time.Second)
- if err != nil {
- return 0
- }
- conn.Close()
- return port
- }
- /*
- Perform a port scan sweep across an entire subnet
- :param ip: the IPv4 address WITH CIDR notation
- :param portmap: the mapping of ports to scan with (port number mapped to protocol name)
- */
- func NetSweep(ips []net.IP, cidr int, ports []int, scanned chan Host) {
- wg := &sync.WaitGroup{}
- network := getNetwork(ips[0].String(), cidr)
- for i := range ips {
- wg.Add(1)
- go func(target string, ntwrk string, portnum []int, wgrp *sync.WaitGroup, output chan Host) {
- defer wgrp.Done()
- portscanned := PortWalk(target, portnum)
- output <- Host{
- Fqdn: getFqdn(target),
- IpAddress: target,
- ListeningPorts: portscanned,
- PortString: strings.Trim(strings.Join(strings.Fields(fmt.Sprint(portscanned)), ","), "[]"),
- Network: ntwrk,
- }
- }(ips[i].String(), network, ports, wg, scanned)
- }
- wg.Wait()
- close(scanned)
- }
- func getFqdn(ip string) string {
- names, err := net.LookupAddr(ip)
- if err != nil {
- return NoFqdn
- }
- return strings.Join(names, ", ")
- }
- func NewSocket() int {
- fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_IP)))
- if err != nil {
- log.Fatal("Could not create a new socket ", err)
- }
- return fd
- }
- func BuildAndSend(intf string, fd int, srcMac net.HardwareAddr, dstMac net.HardwareAddr, srcIp net.IP, dstIp net.IP, srcPort int, dstPort int) {
- ifi, err := net.InterfaceByName(intf)
- if err != nil {
- log.Fatal("Interface was not found on the host: ", err)
- }
- config, err := NewPacketConfig(
- WithEthernetLayer(srcMac, dstMac),
- WithIpLayer(srcIp, dstIp),
- WithPayloadSize(1024),
- )
- if err != nil {
- log.Fatal("error configuring packet: ", err)
- }
- addr := &syscall.SockaddrLinklayer{
- Protocol: htons(syscall.ETH_P_IP),
- Ifindex: ifi.Index,
- }
- // build the packet
- packet, err := BuildPacket(config)
- if err != nil {
- log.Fatal("failed to build packet: %w", err)
- }
- if err = syscall.Sendto(fd, packet, 0, addr); err != nil {
- log.Fatal("Could not send packet out to target, ", err)
- }
- }
- // htons converts a uint16 from host- to network byte order.
- func htons(i uint16) uint16 {
- return (i<<8)&0xff00 | i>>8
- }
|