diff --git a/README.md b/README.md deleted file mode 100644 index c49b386..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# this is NOT for scraping public IPs -i had like zero clue that it was a legal gray zone in europe so like i suggest you do NOT use it on public ips \ No newline at end of file diff --git a/compile.sh b/compile.sh deleted file mode 100644 index a1bcf7b..0000000 --- a/compile.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -# Existing builds -GOOS=darwin GOARCH=arm64 go build -o bin/portscraper_OSX_ARM64 main.go -GOOS=darwin GOARCH=amd64 go build -o bin/portscraper_OSX_X64 main.go -GOOS=windows GOARCH=amd64 go build -o bin/portscraper_WIN_X64_86.exe main.go -GOOS=windows GOARCH=386 go build -o bin/portscraper_WIN_X86.exe main.go -GOOS=freebsd GOARCH=amd64 go build -o bin/portscraper_FREEBSD_X64 main.go -GOOS=freebsd GOARCH=386 go build -o bin/portscraper_FREEBSD_X32 main.go -GOOS=freebsd GOARCH=arm64 go build -o bin/portscraper_FREEBSD_ARM64 main.go -GOOS=linux GOARCH=386 go build -o bin/portscraper_LINUX_X32 main.go -GOOS=linux GOARCH=amd64 go build -o bin/portscraper_LINUX_X64 main.go -GOOS=linux GOARCH=arm64 go build -o bin/portscraper_LINUX_ARM64 main.go -GOOS=linux GOARCH=arm go build -o bin/portscraper_LINUX_ARM main.go -GOOS=linux GOARCH=mipsle go build -o bin/portscraper_LINUX_MIPSLE main.go -GOOS=linux GOARCH=mips go build -o bin/portscraper_LINUX_MIPS main.go - -# my stupid fucking router GL.iNet GL-E750 -GOOS=linux GOARCH=mips GOMIPS=softfloat CGO_ENABLED=0 go build \ - -ldflags="-s -w -extldflags '-static'" \ - -o bin/portscraper_LINUX_MIPS_SOFT main.go \ No newline at end of file diff --git a/main.go b/main.go deleted file mode 100644 index d7e5636..0000000 --- a/main.go +++ /dev/null @@ -1,370 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "net" - "os" - "strconv" - "strings" - "sync" - "time" -) - -type OpenPort struct { - IP string - Port int - Hostname string - Fingerprint string -} - -const batchSize = 255 -var minecraftServers []string -var ( - printMutex sync.Mutex - fileLock sync.Mutex - openPorts []OpenPort - openPortsLock sync.Mutex - ipLineMap = make(map[string]int) - openPortMsgs []string - summaryFile *os.File -) - -func clearScreen() { - fmt.Print("\033[2J") -} - -func moveCursor(row, col int) { - fmt.Printf("\033[%d;%dH", row, col) -} - -func clearLine() { - fmt.Print("\033[K") -} - -func safePrint(format string, a ...interface{}) { - printMutex.Lock() - defer printMutex.Unlock() - fmt.Printf(format, a...) -} - -func safePrintln(a ...interface{}) { - printMutex.Lock() - defer printMutex.Unlock() - fmt.Println(a...) -} - -func printStatusLine(ip, msg string) { - if line, ok := ipLineMap[ip]; ok { - moveCursor(line, 0) - clearLine() - fmt.Printf("[%s] %s", ip, msg) - } -} - -func printOpenPortLine(msg string) { - openPortsLock.Lock() - defer openPortsLock.Unlock() - openPortMsgs = append(openPortMsgs, msg) - moveCursor(len(ipLineMap)+2+len(openPortMsgs), 0) - clearLine() - fmt.Println(msg) -} - -func grabBanner(ip string, port int) string { - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), 1*time.Second) - if err != nil { - return "" - } - defer conn.Close() - conn.SetReadDeadline(time.Now().Add(1 * time.Second)) - buf := make([]byte, 1024) - n, _ := conn.Read(buf) - return strings.TrimSpace(string(buf[:n])) -} - -func identifyServiceAndOS(ip string, port int) string { - data, err := os.ReadFile("services.json") - if err != nil { - return "" - } - var services map[string]map[string]string - json.Unmarshal(data, &services) - - portStr := strconv.Itoa(port) - if info, ok := services[portStr]; ok { - banner := grabBanner(ip, port) - if banner != "" { - return fmt.Sprintf("%s banner: %s", info["name"], banner) - } - return info["name"] - } - return "" -} - -func queryMinecraftServer(ip string, port int) string { - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), 3*time.Second) - if err != nil { - return "" - } - defer conn.Close() - writeVarInt := func(val int) []byte { - var out []byte - for { - temp := byte(val & 0x7F) - val >>= 7 - if val != 0 { - temp |= 0x80 - } - out = append(out, temp) - if val == 0 { - break - } - } - return out - } - - protocolVersion := 754 - serverAddr := ip - state := 1 - // so confusing for me i actually had to comment stuff - var payload []byte - payload = append(payload, 0x00) // fackin packet ID for handshake - payload = append(payload, writeVarInt(protocolVersion)...) // protocol version - payload = append(payload, writeVarInt(len(serverAddr))...) // address length - payload = append(payload, []byte(serverAddr)...) // address - payload = append(payload, byte(port>>8), byte(port&0xFF)) // port - payload = append(payload, byte(state)) // next state: status - - packet := append(writeVarInt(len(payload)), payload...) // full packet = length + payload - conn.Write(packet) // send handshake - conn.Write([]byte{0x01, 0x00}) // send status request - - conn.SetReadDeadline(time.Now().Add(3 * time.Second)) - buf := make([]byte, 4096) - n, err := conn.Read(buf) - if err != nil || n == 0 { - return "" - } - - start := bytes.IndexByte(buf, '{') - if start == -1 { - return "" - } - jsonData := string(buf[start:n]) - - var status struct { - Version struct { - Name string `json:"name"` - } `json:"version"` - Description interface{} `json:"description"` - Players struct { - Online int `json:"online"` - Max int `json:"max"` - } `json:"players"` - } - - if err := json.Unmarshal([]byte(jsonData), &status); err != nil { - return "" - } - - desc := "" - switch v := status.Description.(type) { - case string: - desc = v - case map[string]interface{}: - if text, ok := v["text"].(string); ok { - desc = text - } - } - - return fmt.Sprintf("%s | %d/%d players | %s", status.Version.Name, status.Players.Online, status.Players.Max, desc) -} - - -func scanPort(ip string, port int, wg *sync.WaitGroup) { - printStatusLine(ip, fmt.Sprintf("scanning port %d...", port)) - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), 500*time.Millisecond) - if err == nil { - conn.Close() - host, err := net.LookupAddr(ip) - hostname := "unknown hostname" - if err == nil && len(host) > 0 { - hostname = strings.TrimSuffix(host[0], ".") - } - fingerprint := identifyServiceAndOS(ip, port) - if port == 25565 { - minecraftServers = append(minecraftServers, ip) - } - openPortsLock.Lock() - openPorts = append(openPorts, OpenPort{ip, port, hostname, fingerprint}) - line := fmt.Sprintf(" - %s : port %d open - hostname: %s", ip, port, hostname) - if fingerprint != "" { - line += " | " + fingerprint - } - - fileLock.Lock() - summaryFile.WriteString(line + "\n") - fileLock.Unlock() - openPortsLock.Unlock() - - msg := fmt.Sprintf("[%s] port %d is OPEN (%s)", ip, port, hostname) - if fingerprint != "" { - msg += " | " + fingerprint - } - printOpenPortLine(msg) - printStatusLine(ip, fmt.Sprintf("open port found: %d", port)) - } -} - -func scanPortsForIP(ip string, portStart, portEnd int) { - const maxGoroutines = 50 - guard := make(chan struct{}, maxGoroutines) - var wg sync.WaitGroup - - for port := portStart; port <= portEnd; port++ { - guard <- struct{}{} - wg.Add(1) - go func(p int) { - defer wg.Done() - scanPort(ip, p, &wg) - <-guard - }(port) - } - wg.Wait() - printStatusLine(ip, "scan done.") -} - -func ipRange(start, end string) ([]string, error) { - startIP := net.ParseIP(start).To4() - endIP := net.ParseIP(end).To4() - if startIP == nil || endIP == nil { - return nil, fmt.Errorf("invalid IP") - } - - var ips []string - for ip := startIP; !ipAfter(ip, endIP); ip = nextIP(ip) { - ips = append(ips, ip.String()) - } - return ips, nil -} - -func ipAfter(a, b net.IP) bool { - for i := 0; i < 4; i++ { - if a[i] > b[i] { - return true - } - if a[i] < b[i] { - return false - } - } - return false -} - -func nextIP(ip net.IP) net.IP { - next := make(net.IP, len(ip)) - copy(next, ip) - for i := 3; i >= 0; i-- { - next[i]++ - if next[i] != 0 { - break - } - } - return next -} - -func main() { - var err error - summaryFile, err = os.Create("summary.txt") - if err != nil { - fmt.Println("[!] failed to open summary file:", err) - os.Exit(1) - } - defer summaryFile.Close() - - reader := bufio.NewReader(os.Stdin) - fmt.Print("enter IP range (e.g., 192.168.1.1-192.168.1.20): ") - ipInput, _ := reader.ReadString('\n') - ipInput = strings.TrimSpace(ipInput) - if ipInput == "" { - ipInput = "192.168.0.0-192.168.255.255" - } - fmt.Print("port range (e.g., 1-65535): ") - portInput, _ := reader.ReadString('\n') - portInput = strings.TrimSpace(portInput) - if portInput == "" { - portInput = "1-10000" - } - ipParts := strings.Split(ipInput, "-") - portParts := strings.Split(portInput, "-") - if len(ipParts) != 2 || len(portParts) != 2 { - fmt.Println("[!] bad input.") - os.Exit(1) - } - ipList, err := ipRange(ipParts[0], ipParts[1]) - if err != nil { - fmt.Println("[!] bad IP range:", err) - os.Exit(1) - } - portStart, _ := strconv.Atoi(portParts[0]) - portEnd, _ := strconv.Atoi(portParts[1]) - - fmt.Println("--------------------------------------------------") - fmt.Printf("scanning %s to %s\n", ipParts[0], ipParts[1]) - fmt.Printf("ports %d to %d\n", portStart, portEnd) - fmt.Println("started at:", time.Now()) - fmt.Println("--------------------------------------------------") - - clearScreen() - for idx, ip := range ipList { - ipLineMap[ip] = idx + 1 - printStatusLine(ip, "waiting to scan...") - } - - var wg sync.WaitGroup - for i := 0; i < len(ipList); i += batchSize { - end := i + batchSize - if end > len(ipList) { - end = len(ipList) - } - batch := ipList[i:end] - for _, ip := range batch { - wg.Add(1) - go func(ip string) { - defer wg.Done() - scanPortsForIP(ip, portStart, portEnd) - }(ip) - } - wg.Wait() - } - - moveCursor(len(ipLineMap)+3+len(openPortMsgs), 0) - clearLine() - fmt.Println("\n--------------------------------------------------") - fmt.Println("scan done at:", time.Now()) - fmt.Println("--------------------------------------------------") - if len(minecraftServers) > 0 { - safePrintln("[*] querying Minecraft servers on port 25565...") - for _, ip := range minecraftServers { - status := queryMinecraftServer(ip, 25565) - if status != "" { - safePrintln("[MC] Server at", ip, "responded.") - summaryFile.WriteString("[MC] " + ip + ":25565 " + status + "\n") - } else { - summaryFile.WriteString("[MC] " + ip + ":25565 no response or malformed\n") - } - } - } - if len(openPorts) == 0 { - summaryFile.WriteString("no open ports found.\n") - } else { - summaryFile.WriteString("[+] scan summary: open ports found with fingerprints above\n") - summaryFile.WriteString(fmt.Sprintf("[+] scanned %s to %s\n[+] ports %d to %d\n[+] %d scanned\n[+] %d hits\n", ipParts[0], ipParts[1], portStart, portEnd, len(ipList), len(openPorts))) - - - } - fmt.Println("[+] scan summary written to summary.txt") - -} diff --git a/main.py b/main.py new file mode 100644 index 0000000..f0f3aac --- /dev/null +++ b/main.py @@ -0,0 +1,253 @@ +import socket +from datetime import datetime +import ipaddress +import sys +import subprocess +import platform +from concurrent.futures import ThreadPoolExecutor, as_completed +import threading +import queue +import json + +log_queue = queue.Queue() + +print_lock = threading.Lock() +ip_status_lock = threading.Lock() +shutdown_event = threading.Event() +ip_line_map = {} +open_ports = [] +open_ports_lock = threading.Lock() +open_port_msgs = [] +open_port_msgs_lock = threading.Lock() +bottom_log_start_line = 0 + +def clear_screen(): + sys.stdout.write("\033[2J") + sys.stdout.flush() + +def move_cursor(row, col=0): + sys.stdout.write(f"\033[{row};{col}H") + +def clear_line(): + sys.stdout.write("\033[K") + +def print_status_line(ip, msg): + with ip_status_lock: + line = ip_line_map.get(ip) + if line: + move_cursor(line) + clear_line() + sys.stdout.write(f"[{ip}] {msg}") + sys.stdout.flush() + +def print_open_port_line(msg): + with open_port_msgs_lock: + open_port_msgs.append(msg) + bottom_line = len(ip_line_map) + 2 + len(open_port_msgs) + move_cursor(bottom_line) + clear_line() + sys.stdout.write(msg + "\n") + sys.stdout.flush() + +def ping_ip(ip, ping_cmd): + try: + cmd = ping_cmd + [ip] + result = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=1) + return result.returncode == 0 + except subprocess.TimeoutExpired: + return False + except: + return False + + +def is_private_ip(ip): + return ipaddress.IPv4Address(ip).is_private + +def grab_banner(ip, port): + try: + s = socket.socket() + s.settimeout(1) + s.connect((ip, port)) + if port == 22: + banner = s.recv(1024).decode(errors='ignore').strip() + return banner + elif port == 5900: + banner = s.recv(12).decode(errors='ignore').strip() + return banner + else: + return None + except: + return None + finally: + s.close() + +def identify_service_and_os(ip, port): + json_path = "services.json" + try: + with open(json_path, "r") as f: + data = json.load(f) + services = {int(k): v for k, v in data.items()} + except Exception: + services = {} + + service_info = services.get(port) + fingerprint = None + if service_info: + banner = grab_banner(ip, port) + if banner: + fingerprint = f"{service_info['name']} banner: {banner}" + else: + fingerprint = f"{service_info['name']}" + + return fingerprint + +def scan_port(ip, port): + print_status_line(ip, f"scanning port {port}...") + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(0.5) + try: + result = s.connect_ex((ip, port)) + if result == 0: + try: + hostname = socket.gethostbyaddr(ip)[0] + except socket.herror: + hostname = "unknown hostname" + fingerprint = identify_service_and_os(ip, port) + with open_ports_lock: + open_ports.append((ip, port, hostname, fingerprint)) + msg = f"[{ip}] port {port} is OPEN ({hostname})" + if fingerprint: + msg += f" | {fingerprint}" + print_open_port_line(msg) + print_status_line(ip, f"open port found: {port}") + finally: + s.close() + +def scan_ports_for_ip(ip, port_start, port_end): + with ThreadPoolExecutor(max_workers=10) as p_exec: + futures = [p_exec.submit(scan_port, ip, port) for port in range(port_start, port_end + 1)] + for future in as_completed(futures): + if shutdown_event.is_set(): + break + try: + future.result() + except Exception as e: + with print_lock: + print(f"[!] port scan err on {ip}: {e}") + print_status_line(ip, "scan done.") + +def ping_ip_threaded(ip, ping_cmd): + if shutdown_event.is_set(): + return None + return ip if ping_ip(ip, ping_cmd) else None + +def ping_ips_concurrently(ips, ping_cmd, max_workers=50): + alive_ips = [] + with ThreadPoolExecutor(max_workers=max_workers) as executor: + futures = {executor.submit(ping_ip_threaded, ip, ping_cmd): ip for ip in ips} + for future in as_completed(futures): + if shutdown_event.is_set(): + break + ip = futures[future] + try: + result = future.result() + if result: + alive_ips.append(ip) + print(f"[+] found {ip}") + else: + print(f"[~] no response from {ip}") + except Exception as e: + print(f"[!] ping error for {ip}: {e}") + return alive_ips + +def main(): + try: + ip_range = input("enter ip range (e.g., 192.168.1.1-192.168.1.20) [default 192.168.0.0-192.168.255.255]: ").strip() + port_range = input("port range (e.g., 1-65535) [default 1-10000]: ").strip() + if ip_range == "": + ip_start_str, ip_end_str = "192.168.0.0", "192.168.255.255" + else: + ip_start_str, ip_end_str = ip_range.split('-') + if port_range == "": + port_start, port_end = 1, 10000 + else: + port_start, port_end = map(int, port_range.split('-')) + ip_start = ipaddress.IPv4Address(ip_start_str) + ip_end = ipaddress.IPv4Address(ip_end_str) + if ip_start > ip_end: + raise ValueError("start ip > end ip") + except Exception as e: + print(f"[!] bad input: {e}") + sys.exit(1) + + print("-"*60) + print(f"scanning {ip_start} to {ip_end}") + print(f"ports {port_start} to {port_end}") + print("started at:", str(datetime.now())) + print("-"*60) + + osname = platform.system().lower() + if osname == "windows": + ping_cmd = ["ping", "-n", "1", "-w", "1000"] + elif osname == "darwin": + ping_cmd = ["ping", "-c", "1"] + else: + ping_cmd = ["ping", "-c", "1", "-W", "1"] + + all_ips = [str(ipaddress.IPv4Address(i)) for i in range(int(ip_start), int(ip_end)+1)] + + any_public = False + for ip_str in all_ips: + ip_obj = ipaddress.IPv4Address(ip_str) + if not (ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_reserved): + any_public = True + break + + if any_public: + print("[~] public IPs detected, skipping ping and scanning directly...") + ips_to_scan = all_ips + else: + private_ips = [ip for ip in all_ips if is_private_ip(ip)] + print("[~] pinging private IPs...") + alive_ips = ping_ips_concurrently(private_ips, ping_cmd) + if not alive_ips: + print("[-] no IPs responded to ping. exiting.") + sys.exit(0) + ips_to_scan = alive_ips + + clear_screen() + for idx, ip in enumerate(ips_to_scan): + ip_line_map[ip] = idx + 1 + for ip in ips_to_scan: + print_status_line(ip, "waiting to scan...") + for ip in ips_to_scan: + if shutdown_event.is_set(): + break + scan_ports_for_ip(ip, port_start, port_end) + + bottom_line = len(ip_line_map) + 3 + len(open_port_msgs) + move_cursor(bottom_line, 0) + clear_line() + + print("\n" + "-"*60) + print("scan done at:", str(datetime.now())) + print("-"*60 + "\n") + + with open("summary.txt", "w") as f: + if open_ports: + f.write("[+] scan summary: open ports found with fingerprints\n") + for ip, port, host, fingerprint in open_ports: + f.write(f" - {ip} : port {port} open - hostname: {host}") + if fingerprint: + f.write(f" | {fingerprint}") + f.write("\n") + else: + f.write("no open ports found.\n") + + print("[+] scan summary written to summary.txt :p\n") +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print("\n[!] ctrl+c caught. stopping...") + sys.exit(1) diff --git a/services.json b/services.json index 8cb0264..d6f6b2a 100644 --- a/services.json +++ b/services.json @@ -11,7 +11,6 @@ "143": {"name": "IMAP"}, "161": {"name": "SNMP"}, "445": {"name": "SMB (Windows File Sharing)"}, - "443": {"name": "HTTPS"}, "1433": {"name": "MSSQL"}, "1521": {"name": "Oracle DB"}, "3306": {"name": "MySQL"},