/*
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 main
import (
"flag"
"fmt"
"log"
"net"
"os"
"strings"
"sync"
kyoketsu "git.aetherial.dev/aeth/kyoketsu/pkg"
)
var licenseMsg = "\n http-wokou Copyright (C) 2024 Russell Hrubesky, ChiralWorks Software LLC\n This program comes with ABSOLUTELY NO WARRANTY; for details type `http-wokou --license`\n This is free software, and you are welcome to redistribute it\n under certain conditions; type `http-wokou --redist` for details.\n\n"
var redistMsg = "\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n"
var licenseMsgLong = "\n GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n http-wokou, An HTTP Proxying framework for bypassing DNS Security\n Copyright (C) 2024 Russell Hrubesky, ChiralWorks Software LLC\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n"
func main() {
ip := flag.String("ips", "", "single ip address with CIDR notation to gather info about")
licenseInfo := flag.Bool("license", false, "Pass this flag to display license and warantee information.")
redistInfo := flag.Bool("redist", false, "Pass this flag to display redistribution information.")
flag.Parse()
if *licenseInfo {
fmt.Println(licenseMsgLong)
os.Exit(0)
}
if *redistInfo {
fmt.Println(redistMsg)
os.Exit(0)
}
fmt.Println(licenseMsg)
var err error
var addr *kyoketsu.IpSubnetMapper
addr, err = kyoketsu.GetNetworkAddresses(*ip)
if err != nil {
log.Fatal(err)
}
var wg sync.WaitGroup
for i := range addr.Ipv4s {
wg.Add(1)
go func(target string, wg *sync.WaitGroup) {
defer wg.Done()
out := kyoketsu.PortWalk(target, kyoketsu.RetrieveScanDirectives().Pairs)
if len(out.ListeningPorts) > 0 {
dns, _ := net.LookupAddr(out.IpAddress)
out.Fqdn = strings.Join(dns, ", ")
fmt.Printf("%+v\n", out)
}
}(addr.Ipv4s[i].String(), &wg)
}
wg.Wait()
}
/*
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"
"net/netip"
"strconv"
"strings"
)
type NetworkInterfaceNotFound struct{ Passed string }
// Implementing error interface
func (n *NetworkInterfaceNotFound) Error() string {
return fmt.Sprintf("Interface: '%s' not found.", n.Passed)
}
type IpSubnetMapper struct {
Ipv4s []net.IP `json:"addresses"`
NetworkAddr net.IP
Current net.IP
Mask int
}
/*
Get the next IPv4 address of the address specified in the 'addr' argument,
:param addr: the address to get the next address of
*/
func getNextAddr(addr string) string {
parsed, err := netip.ParseAddr(addr)
if err != nil {
log.Fatal("failed while parsing address in getNextAddr() ", err, "\n")
}
return parsed.Next().String()
}
/*
get the network address of the ip address in 'addr' with the subnet mask from 'cidr'
:param addr: the ipv4 address to get the network address of
:param cidr: the CIDR notation of the subbet
*/
func getNetwork(addr string, cidr int) string {
addr = fmt.Sprintf("%s/%v", addr, cidr)
ip, net, err := net.ParseCIDR(addr)
if err != nil {
log.Fatal("failed whilst attempting to parse cidr in getNetwork() ", err, "\n")
}
return ip.Mask(net.Mask).String()
}
/*
Recursive function to get all of the IPv4 addresses for each IPv4 network that the host is on
:param ipmap: a pointer to an IpSubnetMapper struct which contains domain details such as
the subnet mask, the original network mask, and the current IP address used in the
recursive function
:param max: This is safety feature to prevent stack overflows, so you can manually set the depth to
call the function
*/
func addressRecurse(ipmap *IpSubnetMapper) {
next := getNextAddr(ipmap.Current.String())
nextNet := getNetwork(next, ipmap.Mask)
currentNet := ipmap.NetworkAddr.String()
if nextNet != currentNet {
return
}
ipmap.Current = net.ParseIP(next)
ipmap.Ipv4s = append(ipmap.Ipv4s, net.ParseIP(next))
addressRecurse(ipmap)
}
/*
Get all of the IPv4 addresses in the network that 'addr' belongs to. YOU MUST PASS THE ADDRESS WITH CIDR NOTATION
i.e. '192.168.50.1/24'
:param addr: the ipv4 address to use for subnet discovery
*/
func GetNetworkAddresses(addr string) (*IpSubnetMapper, error) {
ipmap := &IpSubnetMapper{Ipv4s: []net.IP{}}
ip, net, err := net.ParseCIDR(addr)
if err != nil {
return nil, err
}
mask, err := strconv.Atoi(strings.Split(addr, "/")[1])
if err != nil {
return nil, err
}
ipmap.NetworkAddr = ip.Mask(net.Mask)
ipmap.Mask = mask
ipmap.Current = ip.Mask(net.Mask)
addressRecurse(ipmap)
return ipmap, nil
}
/*
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"
"net"
"sync"
"time"
)
var PORT_MAP = map[int]string{
22: "ssh", 23: "telnet", 53: "dns", 80: "http", 25: "smtp", 443: "https", 8080: "unknown", 8081: "unknown",
//8082: "unknown", 8085: "unknown", 8090: "unknown", 8091: "unknown", 9010: "unknown", 9012: "unknown", 10000: "unknown", 1433: "microsoft_sql",
3306: "mysql", 3050: "firebird", 5432: "postgres", 27017: "mongo", 6379: "redis", 8005: "tomcat", 6443: "kubernetes", 853: "dns-tls", 143: "imap",
389: "ldap", 445: "smb", 543: "kerberos", 544: "kerberos", 749: "kerberos", 760: "kerberos",
}
/*
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 map[int]string // list of maps depicting a port number -> service name
}
/*
Perform a concurrent TCP port dial on a host, either by domain name or IP.
:param addr: the address of fqdn to scan
:param portmap: a key/value pair of port numbers to service names to dial the host with
*/
func PortWalk(addr string, portmap map[int]string) *Host {
wg := &sync.WaitGroup{}
out := []*PortScanResult{}
for p, s := range portmap {
wg.Add(1)
go func(target string, p int, s string) {
defer wg.Done()
out = append(out, singlePortScan(target, p, s))
}(addr, p, s)
}
wg.Wait()
host := &Host{IpAddress: addr, ListeningPorts: map[int]string{}}
for i := range out {
if out[i].Listening {
host.ListeningPorts[out[i].PortNumber] = out[i].Service
}
}
return host
}
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
}
type PortScanDirective struct {
// Struct for dependency injecting the dynamic port map used for scans
Pairs map[int]string
}
/*
Wrapper function to dependency inject the resource for a port -> service name mapping.
May move to a database, or something.
*/
func RetrieveScanDirectives() PortScanDirective {
return PortScanDirective{Pairs: PORT_MAP}
}
/*
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, svcs string) *PortScanResult {
address := fmt.Sprintf("%v:%d", addr, port)
conn, err := net.DialTimeout("tcp", address, 5*time.Second)
if err != nil {
return &PortScanResult{PortNumber: port, Protocol: "tcp", Service: svcs, Listening: false}
}
conn.Close()
return &PortScanResult{PortNumber: port, Protocol: "tcp", Service: svcs, Listening: true}
}
/*
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 (
"context"
"fmt"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type TopologyDatabaseIO interface {
/*
This interface defines the Input and output methods that will be necessary
for an appropriate implementation of the data storage that the distributed system will use.
When I get around to implementing the client-to-client format of this, it could be anything.
*/
AddHostToDb(*Host) error // Add a host to the hosts table
UpdateHostEntry(string, *Host) error //Update a host entry, indexing by its ip address
RemoveHostEntry(string) error // Remove a host from the database
}
type MongoClient struct {
conn *mongo.Client
}
func NewMongoClient(host string, port int) *MongoClient {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, options.Client().ApplyURI(fmt.Sprintf("mongodb://%s:%v", host, port)))
defer func() {
if err = client.Disconnect(ctx); err != nil {
panic(err)
}
}()
return &MongoClient{conn: client}
}
func (m *MongoClient) addDocument(id string, data interface{}) error {
return nil
}
func (m *MongoClient) AddHostToDb(host *Host) error {
return nil
}
func (m *MongoClient) UpdateHostEntry(id string, host *Host) error {
return nil
}
func (m *MongoClient) RemoveHostEntry(id string) error {
return nil
}