Compare commits
No commits in common. "main" and "main" have entirely different histories.
5 changed files with 253 additions and 394 deletions
|
@ -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
|
|
21
compile.sh
21
compile.sh
|
@ -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
|
|
370
main.go
370
main.go
|
@ -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")
|
|
||||||
|
|
||||||
}
|
|
253
main.py
Normal file
253
main.py
Normal file
|
@ -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)
|
|
@ -11,7 +11,6 @@
|
||||||
"143": {"name": "IMAP"},
|
"143": {"name": "IMAP"},
|
||||||
"161": {"name": "SNMP"},
|
"161": {"name": "SNMP"},
|
||||||
"445": {"name": "SMB (Windows File Sharing)"},
|
"445": {"name": "SMB (Windows File Sharing)"},
|
||||||
"443": {"name": "HTTPS"},
|
|
||||||
"1433": {"name": "MSSQL"},
|
"1433": {"name": "MSSQL"},
|
||||||
"1521": {"name": "Oracle DB"},
|
"1521": {"name": "Oracle DB"},
|
||||||
"3306": {"name": "MySQL"},
|
"3306": {"name": "MySQL"},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue