📶 [WIP] RouterOS WinBox bruteforce
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mtbf/eparse.go

165 lines
4.6 KiB

package main
import (
"errors"
"net"
"strconv"
"strings"
)
func init() {
registerSwitch("no-ipv6", "skip IPv6 entries")
registerSwitch("append-default-ports", "always append default ports even for targets in host:port format")
registerSwitch("strict-subnets", "strict subnet behaviour: ignore network and broadcast addresses in /30 and bigger subnets")
}
// Safety feature, to avoid expanding subnets into a huge amount of IPs.
const maxNetmaskSize = 22 // expands into /10 for IPv4
// RegisterEndpoint builds an Endpoint and puts it to a global list of endpoints.
func RegisterEndpoint(ip string, ports []int, isIPv6 bool) int {
for _, port := range ports {
ep := Endpoint{addr: Address{ip: ip, port: port, v6: isIPv6}}
ep.loginPos.Init()
ep.passwordPos.Init()
ep.state = ES_Normal
ep.listElement = endpoints.PushBack(&ep)
log("eparse", 3, "registered endpoint: %v", &ep)
}
return len(ports)
}
func incIP(ip net.IP) {
for j := len(ip) - 1; j >= 0; j-- {
ip[j]++
if ip[j] > 0 {
break
}
}
}
// parseCIDR registers multiple endpoints from a CIDR netmask.
func parseCIDR(ip string, ports []int, isIPv6 bool) int {
na, nm, err := net.ParseCIDR(ip)
if err != nil {
log("eparse", 0, "failed to parse CIDR notation for \"%v\": %v", ip, err.Error())
return 0
}
mask, maskBits := nm.Mask.Size()
if mask < maskBits-maxNetmaskSize {
log("eparse", 0, "ignoring out of safe bounds CIDR netmask for \"%v\": %v (max: %v, allowed: %v)", ip, mask, maskBits, maxNetmaskSize)
return 0
}
curHost := 0
maxHost := 1<<(maskBits-mask) - 1
numParsed := 0
strict := getParamSwitch("strict-subnets")
log("eparse", 2, "expanding CIDR: \"%v\" to %v hosts", ip, maxHost+1)
for expIP := na.Mask(nm.Mask); nm.Contains(expIP); incIP(expIP) {
if strict && (curHost == 0 || curHost == maxHost) && maskBits-mask >= 2 {
log("eparse", 1, "ignoring network/broadcast address due to strict-subnets: \"%v\"", expIP.String())
} else {
numParsed += RegisterEndpoint(expIP.String(), ports, isIPv6)
}
curHost++
}
return numParsed
}
// parseIPOrCIDR expands plain IP or CIDR to multiple endpoints.
func parseIPOrCIDR(ip string, ports []int, isIPv6 bool) int {
// ip may be a domain name, a CIDR subnet or an IP address
// CIDR subnets must be expanded to plain IPs
if strings.LastIndex(ip, "/") >= 0 { // this is a CIDR subnet
return parseCIDR(ip, ports, isIPv6)
} else if strings.Count(ip, "/") > 1 { // invalid CIDR notation
log("eparse", 0, "invalid CIDR subnet format: \"%v\", ignoring", ip)
return 0
} else { // otherwise, just register
return RegisterEndpoint(ip, ports, isIPv6)
}
}
// extractIPAndPort extracts all endpoint components.
func extractIPAndPort(str string) (ip string, port int, err error) {
var portString string
ip, portString, err = net.SplitHostPort(str)
if err != nil {
return "", 0, err
}
port, err = strconv.Atoi(portString)
if err != nil {
return "", 0, err
}
if port <= 0 || port > 65535 {
return "", 0, errors.New("invalid port: " + strconv.Itoa(port))
}
return ip, port, nil
}
// ParseEndpoints takes a string slice of IPs/CIDR subnets and converts it to a list of endpoints.
func ParseEndpoints(source []string, name string) {
log("eparse", 1, "parsing endpoints from %v", name)
totalIPv6Skipped := 0
numParsed := 0
for _, str := range source {
str = strings.TrimSpace(str)
if str == "" {
continue
}
if !strings.Contains(str, ":") {
// no ":": this is an ipv4/dn without port,
// parse it with all known ports
numParsed += parseIPOrCIDR(str, getParamIntSlice("port"), false)
} else {
// either ipv4/dn with port, or ipv6 with/without port
isIPv6 := strings.Count(str, ":") > 1
if isIPv6 && getParamSwitch("no-ipv6") {
totalIPv6Skipped++
continue
}
if !strings.Contains(str, "]:") && strings.Contains(str, "::") {
// ipv6 without port
numParsed += parseIPOrCIDR(str, getParamIntSlice("port"), true)
continue
}
ip, port, err := extractIPAndPort(str)
if err != nil {
log("eparse", 0, "failed to extract ip/port for \"%v\": %v, ignoring endpoint", str, err.Error())
continue
}
ports := []int{port}
// append all default ports
if getParamSwitch("append-default-ports") {
for _, port2 := range getParamIntSlice("port") {
if port != port2 {
ports = append(ports, port2)
}
}
}
numParsed += parseIPOrCIDR(ip, ports, isIPv6)
}
}
logIf(totalIPv6Skipped > 0, "eparse", 0, "skipped %v IPv6 targets due to no-ipv6 flag", totalIPv6Skipped)
log("eparse", 1, "finished parsing endpoints from %v: parsed %v out of total %v", name, numParsed, endpoints.Len())
}